From 2d16e7b8910f40070086f3966ce8f9eb55ee8223 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Tue, 21 Feb 2023 13:35:26 +0100 Subject: [PATCH] feat(keycard): keycard details are being synced among devices - sync all keycards state - sync every keycard change --- VERSION | 2 +- multiaccounts/keypairs/database.go | 209 ++++-- multiaccounts/keypairs/database_test.go | 54 +- protocol/messenger.go | 34 + protocol/messenger_keycard.go | 285 ++++++++ protocol/messenger_response.go | 31 + .../messenger_sync_keycard_change_test.go | 620 ++++++++++++++++++ .../messenger_sync_keycards_state_test.go | 437 ++++++++++++ protocol/messenger_sync_raw_messages.go | 11 + .../application_metadata_message.pb.go | 123 ++-- .../application_metadata_message.proto | 2 + protocol/protobuf/pairing.pb.go | 541 ++++++++++----- protocol/protobuf/pairing.proto | 31 + protocol/v1/status_message.go | 4 + services/accounts/accounts.go | 25 +- 15 files changed, 2113 insertions(+), 296 deletions(-) create mode 100644 protocol/messenger_keycard.go create mode 100644 protocol/messenger_sync_keycard_change_test.go create mode 100644 protocol/messenger_sync_keycards_state_test.go diff --git a/VERSION b/VERSION index 4402320d1..32b73a2dc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.133.1 \ No newline at end of file +0.133.2 \ No newline at end of file diff --git a/multiaccounts/keypairs/database.go b/multiaccounts/keypairs/database.go index 65bdbacca..97b32b9c4 100644 --- a/multiaccounts/keypairs/database.go +++ b/multiaccounts/keypairs/database.go @@ -7,11 +7,10 @@ import ( "strings" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/protobuf" ) -const ( - dbTransactionIsNil = "database transaction is nil" -) +var errDbTransactionIsNil = errors.New("keycard: database transaction is nil") type KeyPair struct { KeycardUID string `json:"keycard-uid"` @@ -22,6 +21,47 @@ type KeyPair struct { LastUpdateClock uint64 } +type KeycardAction struct { + Action string `json:"action"` + OldKeycardUID string `json:"old-keycard-uid,omitempty"` + Keycard *KeyPair `json:"keycard"` +} + +func (kp *KeyPair) ToSyncKeycard() *protobuf.SyncKeycard { + kc := &protobuf.SyncKeycard{ + Uid: kp.KeycardUID, + Name: kp.KeycardName, + Locked: kp.KeycardLocked, + KeyUid: kp.KeyUID, + Clock: kp.LastUpdateClock, + } + + for _, addr := range kp.AccountsAddresses { + kc.Addresses = append(kc.Addresses, addr.Bytes()) + } + + return kc +} + +func (kp *KeyPair) FromSyncKeycard(kc *protobuf.SyncKeycard) { + kp.KeycardUID = kc.Uid + kp.KeycardName = kc.Name + kp.KeycardLocked = kc.Locked + kp.KeyUID = kc.KeyUid + kp.LastUpdateClock = kc.Clock + + for _, addr := range kc.Addresses { + kp.AccountsAddresses = append(kp.AccountsAddresses, types.BytesToAddress(addr)) + } +} + +func removeElementAtIndex[T any](s []T, index int) []T { + if index < 0 || index >= len(s) { + panic("keycard: index out of the range") + } + return append(s[:index], s[index+1:]...) +} + type KeyPairs struct { db *sql.DB } @@ -150,7 +190,7 @@ func (kp *KeyPairs) startTransactionAndCheckIfNeedToProceed(kcUID string, clock var dbLastUpdateClock uint64 err = tx.QueryRow(`SELECT last_update_clock FROM keycards WHERE keycard_uid = ?`, kcUID).Scan(&dbLastUpdateClock) if err != nil { - return tx, false, err + return tx, err == sql.ErrNoRows, err } return tx, dbLastUpdateClock <= clock, nil @@ -158,7 +198,7 @@ func (kp *KeyPairs) startTransactionAndCheckIfNeedToProceed(kcUID string, clock func (kp *KeyPairs) setLastUpdateClock(tx *sql.Tx, kcUID string, clock uint64) (err error) { if tx == nil { - return errors.New(dbTransactionIsNil) + return errDbTransactionIsNil } _, err = tx.Exec(` @@ -176,7 +216,7 @@ func (kp *KeyPairs) setLastUpdateClock(tx *sql.Tx, kcUID string, clock uint64) ( func (kp *KeyPairs) getAccountsForKeycard(tx *sql.Tx, kcUID string) ([]types.Address, error) { var accountAddresses []types.Address if tx == nil { - return accountAddresses, errors.New(dbTransactionIsNil) + return accountAddresses, errDbTransactionIsNil } rows, err := tx.Query(`SELECT account_address FROM keycards_accounts WHERE keycard_uid = ?`, kcUID) @@ -199,7 +239,7 @@ func (kp *KeyPairs) getAccountsForKeycard(tx *sql.Tx, kcUID string) ([]types.Add func (kp *KeyPairs) addAccounts(tx *sql.Tx, kcUID string, accountsAddresses []types.Address) (err error) { if tx == nil { - return errors.New(dbTransactionIsNil) + return errDbTransactionIsNil } insertKcAcc, err := tx.Prepare(` @@ -232,7 +272,7 @@ func (kp *KeyPairs) addAccounts(tx *sql.Tx, kcUID string, accountsAddresses []ty func (kp *KeyPairs) deleteKeycard(tx *sql.Tx, kcUID string) (err error) { if tx == nil { - return errors.New(dbTransactionIsNil) + return errDbTransactionIsNil } delete, err := tx.Prepare(` @@ -252,7 +292,7 @@ func (kp *KeyPairs) deleteKeycard(tx *sql.Tx, kcUID string) (err error) { return err } -func (kp *KeyPairs) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair KeyPair) (added bool, err error) { +func (kp *KeyPairs) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair KeyPair) (addedKc bool, addedAccs bool, err error) { tx, proceed, err := kp.startTransactionAndCheckIfNeedToProceed(keyPair.KeycardUID, keyPair.LastUpdateClock) defer func() { if err == nil { @@ -262,8 +302,9 @@ func (kp *KeyPairs) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair KeyP _ = tx.Rollback() }() - if err != nil { - if err == sql.ErrNoRows { + if proceed { + // insert only if there is no such keycard, otherwise just add accounts + if err != nil && err == sql.ErrNoRows { _, err = tx.Exec(` INSERT INTO keycards @@ -279,31 +320,117 @@ func (kp *KeyPairs) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair KeyP keyPair.KeycardUID, keyPair.KeycardName, keyPair.KeycardLocked, keyPair.KeyUID, keyPair.LastUpdateClock) if err != nil { - return false, err + return false, false, err } err = kp.addAccounts(tx, keyPair.KeycardUID, keyPair.AccountsAddresses) - return err == nil, err + return err == nil, false, err } - return false, err - } - - if proceed { err = kp.setLastUpdateClock(tx, keyPair.KeycardUID, keyPair.LastUpdateClock) if err != nil { - return false, err + return false, false, err } err = kp.addAccounts(tx, keyPair.KeycardUID, keyPair.AccountsAddresses) - return err == nil, err + return false, err == nil, err } - return false, nil + return false, false, err +} + +func (kp *KeyPairs) SyncKeycards(syncingClock uint64, keypairsToSync []*KeyPair) (err error) { + tx, err := kp.db.Begin() + if err != nil { + return + } + defer func() { + if err == nil { + err = tx.Commit() + return + } + _ = tx.Rollback() + }() + + rows, err := tx.Query(`SELECT * FROM keycards`) + if err != nil && err != sql.ErrNoRows { + return err + } + defer rows.Close() + + var dbKeypairs []*KeyPair + for rows.Next() { + keyPair := &KeyPair{} + err := rows.Scan(&keyPair.KeycardUID, &keyPair.KeycardName, &keyPair.KeycardLocked, &keyPair.KeyUID, + &keyPair.LastUpdateClock) + if err != nil { + return err + } + + dbKeypairs = append(dbKeypairs, keyPair) + } + + // apply those from `keypairsToSync` which are newer + for _, syncKp := range keypairsToSync { + foundAtIndex := -1 + for i := range dbKeypairs { + if dbKeypairs[i].KeycardUID == syncKp.KeycardUID { + foundAtIndex = i + break + } + } + + doInsertOrReplace := true + if foundAtIndex > -1 { + if dbKeypairs[foundAtIndex].LastUpdateClock > syncKp.LastUpdateClock { + doInsertOrReplace = false + } + dbKeypairs = removeElementAtIndex(dbKeypairs, foundAtIndex) + } + + if doInsertOrReplace { + _, err = tx.Exec(` + INSERT OR REPLACE INTO + keycards + ( + keycard_uid, + keycard_name, + keycard_locked, + key_uid, + last_update_clock + ) + VALUES + (?, ?, ?, ?, ?);`, + syncKp.KeycardUID, syncKp.KeycardName, syncKp.KeycardLocked, syncKp.KeyUID, syncKp.LastUpdateClock) + + if err != nil { + return err + } + + err = kp.addAccounts(tx, syncKp.KeycardUID, syncKp.AccountsAddresses) + if err != nil { + return err + } + } + } + + // remove those from the db if they are not in `keypairsToSync` and if they are older than the moment `keypairsToSync` was created at + for _, dbKp := range dbKeypairs { + if dbKp.LastUpdateClock > syncingClock { + continue + } + + err = kp.deleteKeycard(tx, dbKp.KeycardUID) + if err != nil { + return err + } + } + + return nil } func (kp *KeyPairs) RemoveMigratedAccountsForKeycard(kcUID string, accountAddresses []types.Address, - clock uint64) (removed bool, err error) { + clock uint64) (err error) { tx, proceed, err := kp.startTransactionAndCheckIfNeedToProceed(kcUID, clock) defer func() { if err == nil { @@ -314,18 +441,18 @@ func (kp *KeyPairs) RemoveMigratedAccountsForKeycard(kcUID string, accountAddres }() if err != nil { - return false, err + return err } if proceed { err = kp.setLastUpdateClock(tx, kcUID, clock) if err != nil { - return false, err + return err } dbAccountAddresses, err := kp.getAccountsForKeycard(tx, kcUID) if err != nil { - return false, err + return err } deleteKeycard := true for _, dbAddr := range dbAccountAddresses { @@ -341,8 +468,7 @@ func (kp *KeyPairs) RemoveMigratedAccountsForKeycard(kcUID string, accountAddres } if deleteKeycard { - err = kp.deleteKeycard(tx, kcUID) - return err == nil, err + return kp.deleteKeycard(tx, kcUID) } inVector := strings.Repeat(",?", len(accountAddresses)-1) @@ -357,7 +483,7 @@ func (kp *KeyPairs) RemoveMigratedAccountsForKeycard(kcUID string, accountAddres ` delete, err := tx.Prepare(query) if err != nil { - return false, err + return err } args := make([]interface{}, len(accountAddresses)+1) @@ -370,13 +496,13 @@ func (kp *KeyPairs) RemoveMigratedAccountsForKeycard(kcUID string, accountAddres _, err = delete.Exec(args...) - return true, err + return err } - return false, nil + return err } -func (kp *KeyPairs) execUpdateQuery(kcUID string, clock uint64, field string, value interface{}) (updated bool, err error) { +func (kp *KeyPairs) execUpdateQuery(kcUID string, clock uint64, field string, value interface{}) (err error) { tx, proceed, err := kp.startTransactionAndCheckIfNeedToProceed(kcUID, clock) defer func() { if err == nil { @@ -387,35 +513,35 @@ func (kp *KeyPairs) execUpdateQuery(kcUID string, clock uint64, field string, va }() if err != nil { - return false, err + return err } if proceed { sql := fmt.Sprintf(`UPDATE keycards SET %s = ?, last_update_clock = ? WHERE keycard_uid = ?`, field) // nolint: gosec _, err = tx.Exec(sql, value, clock, kcUID) - return err == nil, err + return err } - return false, nil + return nil } -func (kp *KeyPairs) KeycardLocked(kcUID string, clock uint64) (updated bool, err error) { +func (kp *KeyPairs) KeycardLocked(kcUID string, clock uint64) (err error) { return kp.execUpdateQuery(kcUID, clock, "keycard_locked", true) } -func (kp *KeyPairs) KeycardUnlocked(kcUID string, clock uint64) (updated bool, err error) { +func (kp *KeyPairs) KeycardUnlocked(kcUID string, clock uint64) (err error) { return kp.execUpdateQuery(kcUID, clock, "keycard_locked", false) } -func (kp *KeyPairs) UpdateKeycardUID(oldKcUID string, newKcUID string, clock uint64) (updated bool, err error) { +func (kp *KeyPairs) UpdateKeycardUID(oldKcUID string, newKcUID string, clock uint64) (err error) { return kp.execUpdateQuery(oldKcUID, clock, "keycard_uid", newKcUID) } -func (kp *KeyPairs) SetKeycardName(kcUID string, kpName string, clock uint64) (updated bool, err error) { +func (kp *KeyPairs) SetKeycardName(kcUID string, kpName string, clock uint64) (err error) { return kp.execUpdateQuery(kcUID, clock, "keycard_name", kpName) } -func (kp *KeyPairs) DeleteKeycard(kcUID string, clock uint64) (deleted bool, err error) { +func (kp *KeyPairs) DeleteKeycard(kcUID string, clock uint64) (err error) { tx, proceed, err := kp.startTransactionAndCheckIfNeedToProceed(kcUID, clock) defer func() { if err == nil { @@ -426,15 +552,14 @@ func (kp *KeyPairs) DeleteKeycard(kcUID string, clock uint64) (deleted bool, err }() if err != nil { - return false, err + return err } if proceed { - err = kp.deleteKeycard(tx, kcUID) - return err == nil, err + return kp.deleteKeycard(tx, kcUID) } - return false, nil + return err } func (kp *KeyPairs) DeleteKeypair(keyUID string) (err error) { diff --git a/multiaccounts/keypairs/database_test.go b/multiaccounts/keypairs/database_test.go index d6e83352a..9af84b44f 100644 --- a/multiaccounts/keypairs/database_test.go +++ b/multiaccounts/keypairs/database_test.go @@ -62,34 +62,40 @@ func TestKeypairs(t *testing.T) { } // Test adding key pairs - result, err := db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair1) + addedKc, addedAccs, err := db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair1) require.NoError(t, err) - require.Equal(t, true, result) - result, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair2) + require.Equal(t, true, addedKc) + require.Equal(t, false, addedAccs) + addedKc, addedAccs, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair2) require.NoError(t, err) - require.Equal(t, true, result) - result, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair3) + require.Equal(t, true, addedKc) + require.Equal(t, false, addedAccs) + addedKc, addedAccs, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair3) require.NoError(t, err) - require.Equal(t, true, result) + require.Equal(t, true, addedKc) + require.Equal(t, false, addedAccs) // this should be added - result, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(KeyPair{ + addedKc, addedAccs, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(KeyPair{ KeycardUID: keyPair3.KeycardUID, AccountsAddresses: []types.Address{{0x03}}, LastUpdateClock: keyPair3.LastUpdateClock + 1, }) require.NoError(t, err) - require.Equal(t, true, result) + require.Equal(t, false, addedKc) + require.Equal(t, true, addedAccs) // this should not be added as it has clock value less than last updated clock value - result, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(KeyPair{ + addedKc, addedAccs, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(KeyPair{ KeycardUID: keyPair3.KeycardUID, AccountsAddresses: []types.Address{{0x04}}, LastUpdateClock: keyPair3.LastUpdateClock, }) require.NoError(t, err) - require.Equal(t, false, result) - result, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair4) + require.Equal(t, false, addedKc) + require.Equal(t, false, addedAccs) + addedKc, addedAccs, err = db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(keyPair4) require.NoError(t, err) - require.Equal(t, true, result) + require.Equal(t, true, addedKc) + require.Equal(t, false, addedAccs) // Test reading migrated key pairs rows, err := db.GetAllMigratedKeyPairs() @@ -156,9 +162,8 @@ func TestKeypairs(t *testing.T) { } // Test seting a new keycard name - result, err = db.SetKeycardName(keyPair1.KeycardUID, "Card101", 1000) + err = db.SetKeycardName(keyPair1.KeycardUID, "Card101", 1000) require.NoError(t, err) - require.Equal(t, true, result) rows, err = db.GetAllMigratedKeyPairs() require.NoError(t, err) newKeycardName := "" @@ -170,9 +175,8 @@ func TestKeypairs(t *testing.T) { require.Equal(t, "Card101", newKeycardName) // Test seting a new keycard name with an old clock value - result, err = db.SetKeycardName(keyPair1.KeycardUID, "Card102", 999) // clock is less than the last one + err = db.SetKeycardName(keyPair1.KeycardUID, "Card102", 999) // clock is less than the last one require.NoError(t, err) - require.Equal(t, false, result) rows, err = db.GetAllMigratedKeyPairs() require.NoError(t, err) newKeycardName = "" @@ -184,9 +188,8 @@ func TestKeypairs(t *testing.T) { require.Equal(t, "Card101", newKeycardName) // Test locking a keycard - result, err = db.KeycardLocked(keyPair1.KeycardUID, 1001) + err = db.KeycardLocked(keyPair1.KeycardUID, 1001) require.NoError(t, err) - require.Equal(t, true, result) rows, err = db.GetAllMigratedKeyPairs() require.NoError(t, err) locked := false @@ -201,9 +204,8 @@ func TestKeypairs(t *testing.T) { const numOfAccountsToRemove = 2 require.Greater(t, len(keyPair1.AccountsAddresses), numOfAccountsToRemove) accountsToRemove := keyPair1.AccountsAddresses[:numOfAccountsToRemove] - result, err = db.RemoveMigratedAccountsForKeycard(keyPair1.KeycardUID, accountsToRemove, 1002) + err = db.RemoveMigratedAccountsForKeycard(keyPair1.KeycardUID, accountsToRemove, 1002) require.NoError(t, err) - require.Equal(t, true, result) rows, err = db.GetMigratedKeyPairByKeyUID(keyPair1.KeyUID) require.NoError(t, err) require.Equal(t, 1, len(rows)) @@ -211,9 +213,8 @@ func TestKeypairs(t *testing.T) { // Test deleting accounts one by one, with the last deleted account keycard should be delete as well for i, addr := range keyPair4.AccountsAddresses { - result, err = db.RemoveMigratedAccountsForKeycard(keyPair4.KeycardUID, []types.Address{addr}, 1003+uint64(i)) + err = db.RemoveMigratedAccountsForKeycard(keyPair4.KeycardUID, []types.Address{addr}, 1003+uint64(i)) require.NoError(t, err) - require.Equal(t, true, result) } rows, err = db.GetAllMigratedKeyPairs() require.NoError(t, err) @@ -227,14 +228,12 @@ func TestKeypairs(t *testing.T) { require.Equal(t, true, deletedKeyPair4) // Test update keycard uid - result, err = db.UpdateKeycardUID(keyPair1.KeycardUID, keycardUID, 1100) + err = db.UpdateKeycardUID(keyPair1.KeycardUID, keycardUID, 1100) require.NoError(t, err) - require.Equal(t, true, result) // Test unlocking a locked keycard - result, err = db.KeycardUnlocked(keycardUID, 1101) + err = db.KeycardUnlocked(keycardUID, 1101) require.NoError(t, err) - require.Equal(t, true, result) rows, err = db.GetAllMigratedKeyPairs() require.NoError(t, err) locked = true @@ -246,9 +245,8 @@ func TestKeypairs(t *testing.T) { require.Equal(t, false, locked) // Test detleting a keycard - result, err = db.DeleteKeycard(keycardUID, 1102) + err = db.DeleteKeycard(keycardUID, 1102) require.NoError(t, err) - require.Equal(t, true, result) rows, err = db.GetAllMigratedKeyPairs() require.NoError(t, err) require.Equal(t, 1, len(rows)) diff --git a/protocol/messenger.go b/protocol/messenger.go index 6d23cc297..add35c889 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -2348,6 +2348,12 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string, return err } } + + err = m.syncAllKeycards(ctx, rawMessageHandler) + if err != nil { + return err + } + return nil } @@ -4102,6 +4108,34 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte allMessagesProcessed = false continue } + case protobuf.SyncAllKeycards: + if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { + logger.Warn("not coming from us, ignoring") + continue + } + + p := msg.ParsedMessage.Interface().(protobuf.SyncAllKeycards) + m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p) + err = m.handleSyncKeycards(messageState, p) + if err != nil { + logger.Warn("failed to handle SyncAllKeycards", zap.Error(err)) + allMessagesProcessed = false + continue + } + case protobuf.SyncKeycardAction: + if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { + logger.Warn("not coming from us, ignoring") + continue + } + + p := msg.ParsedMessage.Interface().(protobuf.SyncKeycardAction) + m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p) + err = m.handleSyncKeycardActivity(messageState, p) + if err != nil { + logger.Warn("failed to handle SyncKeycardAction", zap.Error(err)) + allMessagesProcessed = false + continue + } default: // Check if is an encrypted PushNotificationRegistration if msg.Type == protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION { diff --git a/protocol/messenger_keycard.go b/protocol/messenger_keycard.go new file mode 100644 index 000000000..10a8f2379 --- /dev/null +++ b/protocol/messenger_keycard.go @@ -0,0 +1,285 @@ +package protocol + +import ( + "context" + + "github.com/golang/protobuf/proto" + + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/multiaccounts/keypairs" + "github.com/status-im/status-go/protocol/common" + "github.com/status-im/status-go/protocol/protobuf" +) + +func (m *Messenger) dispatchSyncKeycard(ctx context.Context, chatID string, syncKeycard protobuf.SyncAllKeycards, + rawMessageHandler RawMessageHandler) error { + if !m.hasPairedDevices() { + return nil + } + + encodedMessage, err := proto.Marshal(&syncKeycard) + if err != nil { + return err + } + + rawMessage := common.RawMessage{ + LocalChatID: chatID, + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_SYNC_ALL_KEYCARDS, + ResendAutomatically: true, + } + + _, err = rawMessageHandler(ctx, rawMessage) + return err +} + +func (m *Messenger) syncAllKeycards(ctx context.Context, rawMessageHandler RawMessageHandler) (err error) { + allKeycards, err := m.settings.GetAllKnownKeycards() + if err != nil { + return err + } + + clock, chat := m.getLastClockWithRelatedChat() + + message := protobuf.SyncAllKeycards{} + message.Clock = clock + + for _, kc := range allKeycards { + syncKeycard := kc.ToSyncKeycard() + if syncKeycard.Clock == 0 { + syncKeycard.Clock = clock + } + message.Keycards = append(message.Keycards, syncKeycard) + } + + err = m.dispatchSyncKeycard(ctx, chat.ID, message, rawMessageHandler) + if err != nil { + return err + } + + chat.LastClockValue = clock + return m.saveChat(chat) +} + +func (m *Messenger) handleSyncKeycards(state *ReceivedMessageState, syncMessage protobuf.SyncAllKeycards) (err error) { + var keypairsToSync []*keypairs.KeyPair + for _, syncKc := range syncMessage.Keycards { + var kp = &keypairs.KeyPair{} + kp.FromSyncKeycard(syncKc) + keypairsToSync = append(keypairsToSync, kp) + } + + err = m.settings.SyncKeycards(syncMessage.Clock, keypairsToSync) + if err != nil { + return err + } + + allKeycards, err := m.settings.GetAllKnownKeycards() + if err != nil { + return err + } + + state.Response.AddAllKnownKeycards(allKeycards) + + return nil +} + +func (m *Messenger) dispatchKeycardActivity(ctx context.Context, syncMessage protobuf.SyncKeycardAction) error { + if !m.hasPairedDevices() { + return nil + } + + clock, chat := m.getLastClockWithRelatedChat() + + encodedMessage, err := proto.Marshal(&syncMessage) + if err != nil { + return err + } + + rawMessage := common.RawMessage{ + LocalChatID: chat.ID, + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_SYNC_KEYCARD_ACTION, + ResendAutomatically: true, + } + + _, err = m.dispatchMessage(ctx, rawMessage) + if err != nil { + return err + } + + chat.LastClockValue = clock + return m.saveChat(chat) +} + +func (m *Messenger) handleSyncKeycardActivity(state *ReceivedMessageState, syncMessage protobuf.SyncKeycardAction) (err error) { + + var kcAction = &keypairs.KeycardAction{ + Action: protobuf.SyncKeycardAction_Action_name[int32(syncMessage.Action)], + OldKeycardUID: syncMessage.OldKeycardUid, + Keycard: &keypairs.KeyPair{}, + } + kcAction.Keycard.FromSyncKeycard(syncMessage.Keycard) + + switch syncMessage.Action { + case protobuf.SyncKeycardAction_KEYCARD_ADDED, + protobuf.SyncKeycardAction_ACCOUNTS_ADDED: + _, _, err = m.settings.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kcAction.Keycard) + case protobuf.SyncKeycardAction_KEYCARD_DELETED: + err = m.settings.DeleteKeycard(kcAction.Keycard.KeycardUID, kcAction.Keycard.LastUpdateClock) + case protobuf.SyncKeycardAction_ACCOUNTS_REMOVED: + err = m.settings.RemoveMigratedAccountsForKeycard(kcAction.Keycard.KeycardUID, kcAction.Keycard.AccountsAddresses, + kcAction.Keycard.LastUpdateClock) + case protobuf.SyncKeycardAction_LOCKED: + err = m.settings.KeycardLocked(kcAction.Keycard.KeycardUID, kcAction.Keycard.LastUpdateClock) + case protobuf.SyncKeycardAction_UNLOCKED: + err = m.settings.KeycardUnlocked(kcAction.Keycard.KeycardUID, kcAction.Keycard.LastUpdateClock) + case protobuf.SyncKeycardAction_UID_UPDATED: + err = m.settings.UpdateKeycardUID(kcAction.OldKeycardUID, kcAction.Keycard.KeycardUID, + kcAction.Keycard.LastUpdateClock) + case protobuf.SyncKeycardAction_NAME_CHANGED: + err = m.settings.SetKeycardName(kcAction.Keycard.KeycardUID, kcAction.Keycard.KeycardName, + kcAction.Keycard.LastUpdateClock) + default: + panic("unknown action for handling keycard activity") + } + + if err != nil { + return err + } + + state.Response.AddKeycardAction(kcAction) + + return nil +} + +func (m *Messenger) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx context.Context, kp *keypairs.KeyPair) (added bool, err error) { + addedKc, addedAccs, err := m.settings.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + if err != nil { + return addedKc || addedAccs, err + } + + activityMessage := protobuf.SyncKeycardAction{ + Keycard: kp.ToSyncKeycard(), + } + if addedKc { + activityMessage.Action = protobuf.SyncKeycardAction_KEYCARD_ADDED + } else if addedAccs { + activityMessage.Action = protobuf.SyncKeycardAction_ACCOUNTS_ADDED + } + + return addedKc || addedAccs, m.dispatchKeycardActivity(ctx, activityMessage) +} + +func (m *Messenger) RemoveMigratedAccountsForKeycard(ctx context.Context, kcUID string, accountAddresses []string, clock uint64) error { + var addresses []types.Address + for _, addr := range accountAddresses { + addresses = append(addresses, types.HexToAddress(addr)) + } + + err := m.settings.RemoveMigratedAccountsForKeycard(kcUID, addresses, clock) + if err != nil { + return err + } + + activityMessage := protobuf.SyncKeycardAction{ + Action: protobuf.SyncKeycardAction_ACCOUNTS_REMOVED, + Keycard: &protobuf.SyncKeycard{ + Uid: kcUID, + Clock: clock, + }, + } + + for _, addr := range addresses { + activityMessage.Keycard.Addresses = append(activityMessage.Keycard.Addresses, addr.Bytes()) + } + + return m.dispatchKeycardActivity(ctx, activityMessage) +} + +func (m *Messenger) SetKeycardName(ctx context.Context, kcUID string, kpName string, clock uint64) error { + err := m.settings.SetKeycardName(kcUID, kpName, clock) + if err != nil { + return err + } + + activityMessage := protobuf.SyncKeycardAction{ + Action: protobuf.SyncKeycardAction_NAME_CHANGED, + Keycard: &protobuf.SyncKeycard{ + Uid: kcUID, + Name: kpName, + Clock: clock, + }, + } + + return m.dispatchKeycardActivity(ctx, activityMessage) +} + +func (m *Messenger) KeycardLocked(ctx context.Context, kcUID string, clock uint64) error { + err := m.settings.KeycardLocked(kcUID, clock) + if err != nil { + return err + } + + activityMessage := protobuf.SyncKeycardAction{ + Action: protobuf.SyncKeycardAction_LOCKED, + Keycard: &protobuf.SyncKeycard{ + Uid: kcUID, + Clock: clock, + }, + } + + return m.dispatchKeycardActivity(ctx, activityMessage) +} + +func (m *Messenger) KeycardUnlocked(ctx context.Context, kcUID string, clock uint64) error { + err := m.settings.KeycardUnlocked(kcUID, clock) + if err != nil { + return err + } + + activityMessage := protobuf.SyncKeycardAction{ + Action: protobuf.SyncKeycardAction_UNLOCKED, + Keycard: &protobuf.SyncKeycard{ + Uid: kcUID, + Clock: clock, + }, + } + + return m.dispatchKeycardActivity(ctx, activityMessage) +} + +func (m *Messenger) DeleteKeycard(ctx context.Context, kcUID string, clock uint64) error { + err := m.settings.DeleteKeycard(kcUID, clock) + if err != nil { + return err + } + + activityMessage := protobuf.SyncKeycardAction{ + Action: protobuf.SyncKeycardAction_KEYCARD_DELETED, + Keycard: &protobuf.SyncKeycard{ + Uid: kcUID, + Clock: clock, + }, + } + + return m.dispatchKeycardActivity(ctx, activityMessage) +} + +func (m *Messenger) UpdateKeycardUID(ctx context.Context, oldKcUID string, newKcUID string, clock uint64) error { + err := m.settings.UpdateKeycardUID(oldKcUID, newKcUID, clock) + if err != nil { + return err + } + + activityMessage := protobuf.SyncKeycardAction{ + Action: protobuf.SyncKeycardAction_UID_UPDATED, + OldKeycardUid: oldKcUID, + Keycard: &protobuf.SyncKeycard{ + Uid: newKcUID, + Clock: clock, + }, + } + + return m.dispatchKeycardActivity(ctx, activityMessage) +} diff --git a/protocol/messenger_response.go b/protocol/messenger_response.go index ce5d978cf..f14e30851 100644 --- a/protocol/messenger_response.go +++ b/protocol/messenger_response.go @@ -9,6 +9,7 @@ import ( "github.com/status-im/status-go/appmetrics" "github.com/status-im/status-go/images" "github.com/status-im/status-go/multiaccounts/accounts" + "github.com/status-im/status-go/multiaccounts/keypairs" "github.com/status-im/status-go/multiaccounts/settings" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/communities" @@ -69,6 +70,8 @@ type MessengerResponse struct { trustStatus map[string]verification.TrustStatus emojiReactions map[string]*EmojiReaction savedAddresses map[string]*wallet.SavedAddress + keycards []*keypairs.KeyPair + keycardActions []*keypairs.KeycardAction } func (r *MessengerResponse) MarshalJSON() ([]byte, error) { @@ -107,6 +110,8 @@ func (r *MessengerResponse) MarshalJSON() ([]byte, error) { DiscordMessages []*protobuf.DiscordMessage `json:"discordMessages,omitempty"` DiscordMessageAttachments []*protobuf.DiscordMessageAttachment `json:"discordMessageAtachments,omitempty"` SavedAddresses []*wallet.SavedAddress `json:"savedAddresses,omitempty"` + Keycards []*keypairs.KeyPair `json:"keycards,omitempty"` + KeycardActions []*keypairs.KeycardAction `json:"keycardActions,omitempty"` }{ Contacts: r.Contacts, Installations: r.Installations, @@ -138,6 +143,8 @@ func (r *MessengerResponse) MarshalJSON() ([]byte, error) { DiscordCategories: r.DiscordCategories, DiscordChannels: r.DiscordChannels, DiscordOldestMessageTimestamp: r.DiscordOldestMessageTimestamp, + Keycards: r.AllKnownKeycards(), + KeycardActions: r.KeycardActions(), } responseItem.TrustStatus = r.TrustStatus() @@ -262,6 +269,8 @@ func (r *MessengerResponse) IsEmpty() bool { len(r.verificationRequests)+ len(r.RequestsToJoinCommunity) == 0 && len(r.savedAddresses) == 0 && + len(r.keycards) == 0 && + len(r.keycardActions) == 0 && r.currentStatus == nil && r.activityCenterState == nil } @@ -293,6 +302,8 @@ func (r *MessengerResponse) Merge(response *MessengerResponse) error { r.AddEmojiReactions(response.EmojiReactions()) r.AddInstallations(response.Installations) r.AddSavedAddresses(response.SavedAddresses()) + r.AddAllKnownKeycards(response.AllKnownKeycards()) + r.AddKeycardActions(response.KeycardActions()) r.CommunityChanges = append(r.CommunityChanges, response.CommunityChanges...) return nil @@ -424,6 +435,26 @@ func (r *MessengerResponse) SavedAddresses() []*wallet.SavedAddress { return ers } +func (r *MessengerResponse) AddAllKnownKeycards(keycards []*keypairs.KeyPair) { + r.keycards = append(r.keycards, keycards...) +} + +func (r *MessengerResponse) AllKnownKeycards() []*keypairs.KeyPair { + return r.keycards +} + +func (r *MessengerResponse) AddKeycardAction(keycardAction *keypairs.KeycardAction) { + r.keycardActions = append(r.keycardActions, keycardAction) +} + +func (r *MessengerResponse) AddKeycardActions(keycardActions []*keypairs.KeycardAction) { + r.keycardActions = append(r.keycardActions, keycardActions...) +} + +func (r *MessengerResponse) KeycardActions() []*keypairs.KeycardAction { + return r.keycardActions +} + func (r *MessengerResponse) AddNotification(n *localnotifications.Notification) { if r.notifications == nil { r.notifications = make(map[string]*localnotifications.Notification) diff --git a/protocol/messenger_sync_keycard_change_test.go b/protocol/messenger_sync_keycard_change_test.go new file mode 100644 index 000000000..47368706c --- /dev/null +++ b/protocol/messenger_sync_keycard_change_test.go @@ -0,0 +1,620 @@ +package protocol + +import ( + "context" + "crypto/ecdsa" + "testing" + + "github.com/stretchr/testify/suite" + "go.uber.org/zap" + + gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/encryption/multidevice" + "github.com/status-im/status-go/protocol/tt" + "github.com/status-im/status-go/waku" +) + +func TestMessengerSyncKeycardChangeSuite(t *testing.T) { + suite.Run(t, new(MessengerSyncKeycardChangeSuite)) +} + +type MessengerSyncKeycardChangeSuite struct { + suite.Suite + main *Messenger // main instance of Messenger paired with `other` + other *Messenger + privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger + + // If one wants to send messages between different instances of Messenger, + // a single Waku service should be shared. + shh types.Waku + + logger *zap.Logger +} + +func (s *MessengerSyncKeycardChangeSuite) SetupTest() { + s.logger = tt.MustCreateTestLogger() + + config := waku.DefaultConfig + config.MinimumAcceptedPoW = 0 + shh := waku.New(&config, s.logger) + s.shh = gethbridge.NewGethWakuWrapper(shh) + s.Require().NoError(shh.Start()) + + s.main = s.newMessenger(s.shh) + s.privateKey = s.main.identity + // Start the main messenger in order to receive installations + _, err := s.main.Start() + s.Require().NoError(err) + + // Create new device and add main account to + s.other, err = newMessengerWithKey(s.shh, s.main.identity, s.logger, nil) + s.Require().NoError(err) + + // Pair devices (main and other) + imOther := &multidevice.InstallationMetadata{ + Name: "other-device", + DeviceType: "other-device-type", + } + err = s.other.SetInstallationMetadata(s.other.installationID, imOther) + s.Require().NoError(err) + response, err := s.other.SendPairInstallation(context.Background()) + s.Require().NoError(err) + s.Require().NotNil(response) + + // Wait for the message to reach its destination + _, err = WaitOnMessengerResponse( + s.main, + func(r *MessengerResponse) bool { return len(r.Installations) > 0 }, + "installation not received", + ) + s.Require().NoError(err) + + err = s.main.EnableInstallation(s.other.installationID) + s.Require().NoError(err) +} + +func (s *MessengerSyncKeycardChangeSuite) TearDownTest() { + s.Require().NoError(s.other.Shutdown()) + s.Require().NoError(s.main.Shutdown()) +} + +func (s *MessengerSyncKeycardChangeSuite) newMessenger(shh types.Waku) *Messenger { + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + + messenger, err := newMessengerWithKey(s.shh, privateKey, s.logger, nil) + s.Require().NoError(err) + + return messenger +} + +func (s *MessengerSyncKeycardChangeSuite) TestAddingNewKeycards() { + dbOnReceiver := s.other.settings + + // Add key cards on sender + allKeycardsToSync := getKeycardsForTest()[:2] + for _, kp := range allKeycardsToSync { + added, err := s.main.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(context.Background(), kp) + s.Require().NoError(err) + s.Require().Equal(true, added) + } + + // Wait for the response + _, err := WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == len(allKeycardsToSync) + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(len(allKeycardsToSync), len(syncedKeycards)) + s.Require().True(haveSameElements(syncedKeycards, allKeycardsToSync, sameKeycards)) +} + +func (s *MessengerSyncKeycardChangeSuite) TestAddingAccountsToKeycard() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add additional accounts to sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.AccountsAddresses = []types.Address{{0x011}, {0x022}, {0x033}, {0x044}} + + added, err := s.main.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(context.Background(), updatedKeycard) + s.Require().NoError(err) + s.Require().Equal(true, added) + + // Add accounts that we can check for results later + updatedKeycard.AccountsAddresses = append(updatedKeycard.AccountsAddresses, keycardToSync.AccountsAddresses...) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(syncedKeycards[0], updatedKeycard)) +} + +func (s *MessengerSyncKeycardChangeSuite) TestRemovingAccountsFromKeycard() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + var accountAddressesToRemove []string + for _, acc := range keycardToSync.AccountsAddresses[:2] { + accountAddressesToRemove = append(accountAddressesToRemove, acc.String()) + } + + // Remove accounts from sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.AccountsAddresses = updatedKeycard.AccountsAddresses[2:] + + err = s.main.RemoveMigratedAccountsForKeycard(context.Background(), keycardToSync.KeycardUID, + accountAddressesToRemove, keycardToSync.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(updatedKeycard, syncedKeycards[0])) +} + +func (s *MessengerSyncKeycardChangeSuite) TestRemovingAllAccountsFromKeycard() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + var accountAddressesToRemove []string + for _, acc := range keycardToSync.AccountsAddresses { + accountAddressesToRemove = append(accountAddressesToRemove, acc.String()) + } + + // Remove all accounts from sender + err = s.main.RemoveMigratedAccountsForKeycard(context.Background(), keycardToSync.KeycardUID, + accountAddressesToRemove, keycardToSync.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(0, len(syncedKeycards)) +} + +func (s *MessengerSyncKeycardChangeSuite) TestDeleteKeycard() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Remove keycard from sender + err = s.main.DeleteKeycard(context.Background(), keycardToSync.KeycardUID, keycardToSync.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(0, len(syncedKeycards)) +} + +func (s *MessengerSyncKeycardChangeSuite) TestSettingKeycardName() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Set new keycard name to sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.KeycardName = "New Keycard Name" + updatedKeycard.LastUpdateClock = updatedKeycard.LastUpdateClock + 1 + + err = s.main.SetKeycardName(context.Background(), updatedKeycard.KeycardUID, updatedKeycard.KeycardName, + updatedKeycard.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(updatedKeycard, syncedKeycards[0])) +} + +func (s *MessengerSyncKeycardChangeSuite) TestSettingKeycardNameWithOlderClock() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Set new keycard name to sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.KeycardName = "New Keycard Name" + updatedKeycard.LastUpdateClock = updatedKeycard.LastUpdateClock - 1 + + err = s.main.SetKeycardName(context.Background(), updatedKeycard.KeycardUID, updatedKeycard.KeycardName, + updatedKeycard.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(keycardToSync, syncedKeycards[0])) +} + +func (s *MessengerSyncKeycardChangeSuite) TestSettingKeycardLocked() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Set keycard locked on sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.KeycardLocked = true + updatedKeycard.LastUpdateClock = updatedKeycard.LastUpdateClock + 1 + + err = s.main.KeycardLocked(context.Background(), updatedKeycard.KeycardUID, updatedKeycard.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(updatedKeycard, syncedKeycards[0])) +} + +func (s *MessengerSyncKeycardChangeSuite) TestSettingKeycardLockedOlderClock() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Set keycard locked on sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.KeycardLocked = true + updatedKeycard.LastUpdateClock = updatedKeycard.LastUpdateClock - 1 + + err = s.main.KeycardLocked(context.Background(), updatedKeycard.KeycardUID, updatedKeycard.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(keycardToSync, syncedKeycards[0])) +} + +func (s *MessengerSyncKeycardChangeSuite) TestSettingKeycardUnlocked() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + keycardToSync.KeycardLocked = true + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Set keycard unlocked on sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.KeycardLocked = false + updatedKeycard.LastUpdateClock = updatedKeycard.LastUpdateClock + 1 + + err = s.main.KeycardUnlocked(context.Background(), updatedKeycard.KeycardUID, updatedKeycard.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(updatedKeycard, syncedKeycards[0])) +} + +func (s *MessengerSyncKeycardChangeSuite) TestSettingKeycardUnlockedOlderClock() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + keycardToSync.KeycardLocked = true + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Set keycard unlocked on sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.KeycardLocked = false + updatedKeycard.LastUpdateClock = updatedKeycard.LastUpdateClock - 1 + + err = s.main.KeycardLocked(context.Background(), updatedKeycard.KeycardUID, updatedKeycard.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(keycardToSync, syncedKeycards[0])) +} + +func (s *MessengerSyncKeycardChangeSuite) TestUpdatingKeycardUid() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Set keycard unlocked on sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.KeycardUID = "00000000000000000000000000000000" + updatedKeycard.LastUpdateClock = updatedKeycard.LastUpdateClock + 1 + + err = s.main.UpdateKeycardUID(context.Background(), keycardToSync.KeycardUID, updatedKeycard.KeycardUID, + updatedKeycard.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(updatedKeycard, syncedKeycards[0])) +} + +func (s *MessengerSyncKeycardChangeSuite) TestUpdatingKeycardUidOldClock() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycard on sender + keycardToSync := getKeycardsForTest()[:1][0] + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Add the same keycard on receiver + addedKc, addedAccs, err = dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*keycardToSync) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + + // Set keycard unlocked on sender + updatedKeycard := getKeycardsForTest()[:1][0] + updatedKeycard.KeycardUID = "00000000000000000000000000000000" + updatedKeycard.LastUpdateClock = updatedKeycard.LastUpdateClock - 1 + + err = s.main.UpdateKeycardUID(context.Background(), keycardToSync.KeycardUID, updatedKeycard.KeycardUID, + updatedKeycard.LastUpdateClock) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.KeycardActions()) == 1 + }, + "expected to receive keycard activities", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(1, len(syncedKeycards)) + s.Require().True(sameKeycards(keycardToSync, syncedKeycards[0])) +} diff --git a/protocol/messenger_sync_keycards_state_test.go b/protocol/messenger_sync_keycards_state_test.go new file mode 100644 index 000000000..98048e2b2 --- /dev/null +++ b/protocol/messenger_sync_keycards_state_test.go @@ -0,0 +1,437 @@ +package protocol + +import ( + "context" + "crypto/ecdsa" + "testing" + + "github.com/stretchr/testify/suite" + "go.uber.org/zap" + + gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/multiaccounts/keypairs" + "github.com/status-im/status-go/protocol/encryption/multidevice" + "github.com/status-im/status-go/protocol/tt" + "github.com/status-im/status-go/waku" +) + +func TestMessengerSyncKeycardsStateSuite(t *testing.T) { + suite.Run(t, new(MessengerSyncKeycardsStateSuite)) +} + +type MessengerSyncKeycardsStateSuite struct { + suite.Suite + main *Messenger // main instance of Messenger paired with `other` + other *Messenger + privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger + + // If one wants to send messages between different instances of Messenger, + // a single Waku service should be shared. + shh types.Waku + + logger *zap.Logger +} + +func (s *MessengerSyncKeycardsStateSuite) SetupTest() { + s.logger = tt.MustCreateTestLogger() + + config := waku.DefaultConfig + config.MinimumAcceptedPoW = 0 + shh := waku.New(&config, s.logger) + s.shh = gethbridge.NewGethWakuWrapper(shh) + s.Require().NoError(shh.Start()) + + s.main = s.newMessenger(s.shh) + s.privateKey = s.main.identity + // Start the main messenger in order to receive installations + _, err := s.main.Start() + s.Require().NoError(err) + + // Create new device and add main account to + s.other, err = newMessengerWithKey(s.shh, s.main.identity, s.logger, nil) + s.Require().NoError(err) + + // Pair devices (main and other) + imOther := &multidevice.InstallationMetadata{ + Name: "other-device", + DeviceType: "other-device-type", + } + err = s.other.SetInstallationMetadata(s.other.installationID, imOther) + s.Require().NoError(err) + response, err := s.other.SendPairInstallation(context.Background()) + s.Require().NoError(err) + s.Require().NotNil(response) + + // Wait for the message to reach its destination + _, err = WaitOnMessengerResponse( + s.main, + func(r *MessengerResponse) bool { return len(r.Installations) > 0 }, + "installation not received", + ) + s.Require().NoError(err) + + err = s.main.EnableInstallation(s.other.installationID) + s.Require().NoError(err) +} + +func (s *MessengerSyncKeycardsStateSuite) TearDownTest() { + s.Require().NoError(s.other.Shutdown()) + s.Require().NoError(s.main.Shutdown()) +} + +func (s *MessengerSyncKeycardsStateSuite) newMessenger(shh types.Waku) *Messenger { + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + + messenger, err := newMessengerWithKey(s.shh, privateKey, s.logger, nil) + s.Require().NoError(err) + + return messenger +} + +func sameKeycards(a, b *keypairs.KeyPair) bool { + same := a.KeycardUID == b.KeycardUID && + a.KeyUID == b.KeyUID && + a.KeycardName == b.KeycardName && + a.KeycardLocked == b.KeycardLocked && + a.LastUpdateClock == b.LastUpdateClock && + len(a.AccountsAddresses) == len(b.AccountsAddresses) + + if same { + for i := range a.AccountsAddresses { + found := false + for j := range b.AccountsAddresses { + if a.AccountsAddresses[i] == b.AccountsAddresses[j] { + found = true + break + } + } + + if !found { + return false + } + } + } + + return same +} + +func getKeycardsForTest() []*keypairs.KeyPair { + keyPair1 := keypairs.KeyPair{ + KeycardUID: "00000000000000000000000000000001", + KeycardName: "Card01", + KeycardLocked: false, + AccountsAddresses: []types.Address{{0x01}, {0x02}, {0x03}, {0x04}}, + KeyUID: "0000000000000000000000000000000000000000000000000000000000000001", + LastUpdateClock: 100, + } + keyPair2 := keypairs.KeyPair{ + KeycardUID: "00000000000000000000000000000002", + KeycardName: "Card02", + KeycardLocked: false, + AccountsAddresses: []types.Address{{0x01}, {0x02}}, + KeyUID: "0000000000000000000000000000000000000000000000000000000000000002", + LastUpdateClock: 200, + } + keyPair3 := keypairs.KeyPair{ + KeycardUID: "00000000000000000000000000000003", + KeycardName: "Card02 Copy", + KeycardLocked: false, + AccountsAddresses: []types.Address{{0x01}, {0x02}}, + KeyUID: "0000000000000000000000000000000000000000000000000000000000000002", + LastUpdateClock: 300, + } + keyPair4 := keypairs.KeyPair{ + KeycardUID: "00000000000000000000000000000004", + KeycardName: "Card04", + KeycardLocked: false, + AccountsAddresses: []types.Address{{0x01}, {0x02}, {0x03}}, + KeyUID: "0000000000000000000000000000000000000000000000000000000000000004", + LastUpdateClock: 400, + } + + return []*keypairs.KeyPair{&keyPair1, &keyPair2, &keyPair3, &keyPair4} +} + +func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasNoKeycards() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycards on sender + allKeycardsToSync := getKeycardsForTest() + for _, kp := range allKeycardsToSync { + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Trigger's a sync between devices + err := s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.AllKnownKeycards()) == len(allKeycardsToSync) + }, + "expected to receive keycards", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(len(allKeycardsToSync), len(syncedKeycards)) + s.Require().True(haveSameElements(syncedKeycards, allKeycardsToSync, sameKeycards)) +} + +func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasKeycardsOlderThanSender() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycards on sender + allKeycardsToSync := getKeycardsForTest() + for _, kp := range allKeycardsToSync { + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Add keycards on receiver + keycardsOnReceiver := getKeycardsForTest()[:2] + keycardsOnReceiver[0].KeycardName = "CardNameToBeChanged-0" + keycardsOnReceiver[0].AccountsAddresses = keycardsOnReceiver[0].AccountsAddresses[2:3] + keycardsOnReceiver[0].LastUpdateClock = keycardsOnReceiver[0].LastUpdateClock - 1 + keycardsOnReceiver[1].KeycardName = "CardNameToBeChanged-1" + keycardsOnReceiver[1].LastUpdateClock = keycardsOnReceiver[1].LastUpdateClock - 1 + + for _, kp := range keycardsOnReceiver { + addedKc, addedAccs, err := dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Trigger's a sync between devices + err := s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.AllKnownKeycards()) == len(allKeycardsToSync) + }, + "expected to receive keycards", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(len(allKeycardsToSync), len(syncedKeycards)) + s.Require().True(haveSameElements(syncedKeycards, allKeycardsToSync, sameKeycards)) +} + +func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfKeycardsWereDeletedOnSenderSide() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycards on sender + allKeycardsToSync := getKeycardsForTest()[:2] + for _, kp := range allKeycardsToSync { + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Add keycards on receiver + keycardsOnReceiver := getKeycardsForTest() + for _, kp := range keycardsOnReceiver { + addedKc, addedAccs, err := dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Trigger's a sync between devices + err := s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.AllKnownKeycards()) == len(allKeycardsToSync) + }, + "expected to receive keycards", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(len(allKeycardsToSync), len(syncedKeycards)) + s.Require().True(haveSameElements(syncedKeycards, allKeycardsToSync, sameKeycards)) +} + +func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasNewerKeycardsThanTheSameAreDeletedOnSenderSide() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycards on sender + allKeycardsToSync := getKeycardsForTest()[:2] + for _, kp := range allKeycardsToSync { + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Add keycards on receiver + keycardsOnReceiver := getKeycardsForTest() + clock, _ := s.other.getLastClockWithRelatedChat() + keycardsOnReceiver[2].KeycardName = "NewerCardName-2" + keycardsOnReceiver[2].LastUpdateClock = clock + 1000 + keycardsOnReceiver[3].KeycardName = "NewerCardName-3" + keycardsOnReceiver[3].LastUpdateClock = clock + 1000 + for _, kp := range keycardsOnReceiver { + addedKc, addedAccs, err := dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Trigger's a sync between devices + err := s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.AllKnownKeycards()) >= len(allKeycardsToSync) + }, + "expected to receive keycards", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(len(keycardsOnReceiver), len(syncedKeycards)) + for _, kc := range allKeycardsToSync { + s.Require().True(contains(syncedKeycards, kc, sameKeycards)) + } + for _, kc := range keycardsOnReceiver { + s.Require().True(contains(syncedKeycards, kc, sameKeycards)) + } +} + +func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverAndSenderHasNoKeycardsInCommon() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycards on sender + allKeycardsToSync := getKeycardsForTest()[:2] + for _, kp := range allKeycardsToSync { + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Add keycards on receiver + keycardsOnReceiver := getKeycardsForTest()[2:] + clock, _ := s.other.getLastClockWithRelatedChat() + keycardsOnReceiver[0].KeycardName = "NewerCardName-0" + keycardsOnReceiver[0].LastUpdateClock = clock + 1000 + keycardsOnReceiver[1].KeycardName = "NewerCardName-1" + keycardsOnReceiver[1].LastUpdateClock = clock + 1000 + + for _, kp := range keycardsOnReceiver { + addedKc, addedAccs, err := dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Trigger's a sync between devices + err := s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.AllKnownKeycards()) >= len(allKeycardsToSync) + }, + "expected to receive keycards", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(len(allKeycardsToSync)+len(keycardsOnReceiver), len(syncedKeycards)) + for _, kc := range allKeycardsToSync { + s.Require().True(contains(syncedKeycards, kc, sameKeycards)) + } + for _, kc := range keycardsOnReceiver { + s.Require().True(contains(syncedKeycards, kc, sameKeycards)) + } +} + +func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasNewerKeycardThanSender() { + senderDb := s.main.settings + dbOnReceiver := s.other.settings + + // Add keycards on sender + allKeycardsToSync := getKeycardsForTest() + for _, kp := range allKeycardsToSync { + addedKc, addedAccs, err := senderDb.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Add keycards on receiver + keycardsOnReceiver := getKeycardsForTest()[2:] + clock, _ := s.other.getLastClockWithRelatedChat() + keycardsOnReceiver[0].KeycardName = "NewerCardName-0" + keycardsOnReceiver[0].LastUpdateClock = clock + 1000 + keycardsOnReceiver[1].KeycardName = "NewerCardName-1" + keycardsOnReceiver[1].LastUpdateClock = clock + 1000 + + for _, kp := range keycardsOnReceiver { + addedKc, addedAccs, err := dbOnReceiver.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(*kp) + s.Require().NoError(err) + s.Require().Equal(true, addedKc) + s.Require().Equal(false, addedAccs) + } + + // Trigger's a sync between devices + err := s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil) + s.Require().NoError(err) + + // Wait for the response + _, err = WaitOnMessengerResponse( + s.other, + func(r *MessengerResponse) bool { + return len(r.AllKnownKeycards()) == len(allKeycardsToSync) + }, + "expected to receive keycards", + ) + s.Require().NoError(err) + + syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards() + s.Require().NoError(err) + s.Require().Equal(len(allKeycardsToSync), len(syncedKeycards)) + for _, kc := range allKeycardsToSync[:2] { + s.Require().True(contains(syncedKeycards, kc, sameKeycards)) + } + for _, kc := range keycardsOnReceiver { + s.Require().True(contains(syncedKeycards, kc, sameKeycards)) + } +} diff --git a/protocol/messenger_sync_raw_messages.go b/protocol/messenger_sync_raw_messages.go index a9fcd9944..f82bec284 100644 --- a/protocol/messenger_sync_raw_messages.go +++ b/protocol/messenger_sync_raw_messages.go @@ -188,6 +188,17 @@ func (m *Messenger) HandleSyncRawMessages(rawMessages []*protobuf.RawMessage) er m.logger.Error("failed to handleSyncSavedAddress when HandleSyncRawMessages", zap.Error(err)) continue } + case protobuf.ApplicationMetadataMessage_SYNC_ALL_KEYCARDS: + var message protobuf.SyncAllKeycards + err := proto.Unmarshal(rawMessage.GetPayload(), &message) + if err != nil { + return err + } + err = m.handleSyncKeycards(state, message) + if err != nil { + m.logger.Error("failed to handleSyncKeycards when HandleSyncRawMessages", zap.Error(err)) + continue + } } } response, err := m.saveDataAndPrepareResponse(state) diff --git a/protocol/protobuf/application_metadata_message.pb.go b/protocol/protobuf/application_metadata_message.pb.go index 32b3cad0f..af912f104 100644 --- a/protocol/protobuf/application_metadata_message.pb.go +++ b/protocol/protobuf/application_metadata_message.pb.go @@ -84,6 +84,8 @@ const ( ApplicationMetadataMessage_SYNC_SAVED_ADDRESS ApplicationMetadataMessage_Type = 59 ApplicationMetadataMessage_COMMUNITY_CANCEL_REQUEST_TO_JOIN ApplicationMetadataMessage_Type = 60 ApplicationMetadataMessage_CANCEL_CONTACT_VERIFICATION ApplicationMetadataMessage_Type = 61 + ApplicationMetadataMessage_SYNC_ALL_KEYCARDS ApplicationMetadataMessage_Type = 62 + ApplicationMetadataMessage_SYNC_KEYCARD_ACTION ApplicationMetadataMessage_Type = 63 ) var ApplicationMetadataMessage_Type_name = map[int32]string{ @@ -148,6 +150,8 @@ var ApplicationMetadataMessage_Type_name = map[int32]string{ 59: "SYNC_SAVED_ADDRESS", 60: "COMMUNITY_CANCEL_REQUEST_TO_JOIN", 61: "CANCEL_CONTACT_VERIFICATION", + 62: "SYNC_ALL_KEYCARDS", + 63: "SYNC_KEYCARD_ACTION", } var ApplicationMetadataMessage_Type_value = map[string]int32{ @@ -212,6 +216,8 @@ var ApplicationMetadataMessage_Type_value = map[string]int32{ "SYNC_SAVED_ADDRESS": 59, "COMMUNITY_CANCEL_REQUEST_TO_JOIN": 60, "CANCEL_CONTACT_VERIFICATION": 61, + "SYNC_ALL_KEYCARDS": 62, + "SYNC_KEYCARD_ACTION": 63, } func (x ApplicationMetadataMessage_Type) String() string { @@ -290,62 +296,63 @@ func init() { } var fileDescriptor_ad09a6406fcf24c7 = []byte{ - // 908 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x55, 0x5b, 0x73, 0x13, 0x37, - 0x14, 0x6e, 0x20, 0x4d, 0x40, 0xb9, 0xa0, 0x88, 0x5c, 0x9c, 0xbb, 0x31, 0x34, 0x04, 0x68, 0x4d, - 0x0b, 0x6d, 0xa7, 0x2d, 0xe5, 0x41, 0x96, 0x4e, 0x6c, 0xe1, 0x5d, 0x69, 0x91, 0xb4, 0x66, 0xdc, - 0x17, 0xcd, 0x52, 0x5c, 0x26, 0x33, 0x40, 0x3c, 0xc4, 0x3c, 0xe4, 0x9f, 0xf6, 0x57, 0xf4, 0x37, - 0x74, 0xb4, 0x57, 0x27, 0xd9, 0x90, 0xa7, 0x64, 0xcf, 0xf7, 0xe9, 0x48, 0xe7, 0x3b, 0xdf, 0x39, - 0x46, 0xad, 0x64, 0x3c, 0xfe, 0x70, 0xfc, 0x77, 0x32, 0x39, 0x3e, 0xf9, 0xe4, 0x3e, 0x8e, 0x26, - 0xc9, 0xbb, 0x64, 0x92, 0xb8, 0x8f, 0xa3, 0xd3, 0xd3, 0xe4, 0xfd, 0xa8, 0x3d, 0xfe, 0x7c, 0x32, - 0x39, 0x21, 0xb7, 0xd2, 0x3f, 0x6f, 0xbf, 0xfc, 0xd3, 0xfa, 0x6f, 0x19, 0x6d, 0xd1, 0xea, 0x40, - 0x98, 0xf3, 0xc3, 0x8c, 0x4e, 0x76, 0xd0, 0xed, 0xd3, 0xe3, 0xf7, 0x9f, 0x92, 0xc9, 0x97, 0xcf, - 0xa3, 0xc6, 0x4c, 0x73, 0xe6, 0x70, 0x51, 0x57, 0x01, 0xd2, 0x40, 0xf3, 0xe3, 0xe4, 0xec, 0xc3, - 0x49, 0xf2, 0xae, 0x71, 0x23, 0xc5, 0x8a, 0x4f, 0xf2, 0x12, 0xcd, 0x4e, 0xce, 0xc6, 0xa3, 0xc6, - 0xcd, 0xe6, 0xcc, 0xe1, 0xf2, 0xb3, 0x47, 0xed, 0xe2, 0xbe, 0xf6, 0xd5, 0x77, 0xb5, 0xed, 0xd9, - 0x78, 0xa4, 0xd3, 0x63, 0xad, 0x7f, 0x97, 0xd0, 0xac, 0xff, 0x24, 0x0b, 0x68, 0x3e, 0x96, 0x7d, - 0xa9, 0xde, 0x48, 0xfc, 0x0d, 0xc1, 0x68, 0x91, 0xf5, 0xa8, 0x75, 0x21, 0x18, 0x43, 0xbb, 0x80, - 0x67, 0x08, 0x41, 0xcb, 0x4c, 0x49, 0x4b, 0x99, 0x75, 0x71, 0xc4, 0xa9, 0x05, 0x7c, 0x83, 0xec, - 0xa2, 0xcd, 0x10, 0xc2, 0x0e, 0x68, 0xd3, 0x13, 0x51, 0x1e, 0x2e, 0x8f, 0xdc, 0x24, 0x6b, 0x68, - 0x25, 0xa2, 0x42, 0x3b, 0x21, 0x8d, 0xa5, 0x41, 0x40, 0xad, 0x50, 0x12, 0xcf, 0xfa, 0xb0, 0x19, - 0x4a, 0x76, 0x3e, 0xfc, 0x2d, 0xb9, 0x8f, 0xf6, 0x35, 0xbc, 0x8e, 0xc1, 0x58, 0x47, 0x39, 0xd7, - 0x60, 0x8c, 0x3b, 0x52, 0xda, 0x59, 0x4d, 0xa5, 0xa1, 0x2c, 0x25, 0xcd, 0x91, 0xc7, 0xe8, 0x80, - 0x32, 0x06, 0x91, 0x75, 0xd7, 0x71, 0xe7, 0xc9, 0x13, 0xf4, 0x90, 0x03, 0x0b, 0x84, 0x84, 0x6b, - 0xc9, 0xb7, 0xc8, 0x06, 0xba, 0x5b, 0x90, 0xa6, 0x81, 0xdb, 0x64, 0x15, 0x61, 0x03, 0x92, 0x9f, - 0x8b, 0x22, 0xb2, 0x8f, 0xb6, 0x2f, 0xe6, 0x9e, 0x26, 0x2c, 0x78, 0x69, 0x2e, 0x15, 0xe9, 0x72, - 0x01, 0xf1, 0x62, 0x3d, 0x4c, 0x19, 0x53, 0xb1, 0xb4, 0x78, 0x89, 0xdc, 0x43, 0xbb, 0x97, 0xe1, - 0x28, 0xee, 0x04, 0x82, 0x39, 0xdf, 0x17, 0xbc, 0x4c, 0xf6, 0xd0, 0x56, 0xd1, 0x0f, 0xa6, 0x38, - 0x38, 0xca, 0x07, 0xa0, 0xad, 0x30, 0x10, 0x82, 0xb4, 0xf8, 0x0e, 0x69, 0xa1, 0xbd, 0x28, 0x36, - 0x3d, 0x27, 0x95, 0x15, 0x47, 0x82, 0x65, 0x29, 0x34, 0x74, 0x85, 0xb1, 0x3a, 0x93, 0x1c, 0x7b, - 0x85, 0xbe, 0xce, 0x71, 0x1a, 0x4c, 0xa4, 0xa4, 0x01, 0xbc, 0x42, 0xb6, 0xd1, 0xc6, 0x65, 0xf2, - 0xeb, 0x18, 0xf4, 0x10, 0x13, 0xf2, 0x00, 0x35, 0xaf, 0x00, 0xab, 0x14, 0x77, 0x7d, 0xd5, 0x75, - 0xf7, 0xa5, 0xfa, 0xe1, 0x55, 0x5f, 0x52, 0x1d, 0x9c, 0x1f, 0x5f, 0xf3, 0x16, 0x84, 0x50, 0xbd, - 0x12, 0x4e, 0x43, 0xae, 0xf3, 0x3a, 0xd9, 0x44, 0x6b, 0x5d, 0xad, 0xe2, 0x28, 0x95, 0xc5, 0x09, - 0x39, 0x10, 0x36, 0xab, 0x6e, 0x83, 0xac, 0xa0, 0xa5, 0x2c, 0xc8, 0x41, 0x5a, 0x61, 0x87, 0xb8, - 0xe1, 0xd9, 0x4c, 0x85, 0x61, 0x2c, 0x85, 0x1d, 0x3a, 0x0e, 0x86, 0x69, 0x11, 0xa5, 0xec, 0x4d, - 0xd2, 0x40, 0xab, 0x15, 0x34, 0x95, 0x67, 0xcb, 0xbf, 0xba, 0x42, 0xca, 0x6e, 0x2b, 0xf7, 0x4a, - 0x09, 0x89, 0xb7, 0xc9, 0x1d, 0xb4, 0x10, 0x09, 0x59, 0xda, 0x7e, 0xc7, 0xcf, 0x0e, 0x70, 0x51, - 0xcd, 0xce, 0xae, 0x7f, 0x89, 0xb1, 0xd4, 0xc6, 0xa6, 0x18, 0x9d, 0x3d, 0x5f, 0x0b, 0x87, 0x00, - 0xa6, 0xe6, 0x65, 0xdf, 0x9b, 0xaa, 0xce, 0x33, 0xf9, 0xd5, 0xb8, 0x49, 0xb6, 0xd0, 0x3a, 0x95, - 0x4a, 0x0e, 0x43, 0x15, 0x1b, 0x17, 0x82, 0xd5, 0x82, 0xb9, 0x0e, 0xb5, 0xac, 0x87, 0xef, 0x95, - 0x53, 0x95, 0x96, 0xac, 0x21, 0x54, 0x03, 0xe0, 0xb8, 0xe5, 0xbb, 0x56, 0x85, 0xf3, 0xab, 0x8c, - 0x17, 0x90, 0xe3, 0xfb, 0x04, 0xa1, 0xb9, 0x0e, 0x65, 0xfd, 0x38, 0xc2, 0x0f, 0x4a, 0x47, 0x7a, - 0x65, 0x07, 0xbe, 0x52, 0x06, 0xd2, 0x82, 0xce, 0xa8, 0xdf, 0x95, 0x8e, 0xbc, 0x08, 0x67, 0xd3, - 0x08, 0x1c, 0x1f, 0x78, 0xc7, 0xd5, 0x52, 0xb8, 0x30, 0xa1, 0x30, 0x06, 0x38, 0x7e, 0x98, 0x2a, - 0xe1, 0x39, 0x1d, 0xa5, 0xfa, 0x21, 0xd5, 0x7d, 0x7c, 0x48, 0xd6, 0x11, 0xc9, 0x5e, 0x18, 0x00, - 0xd5, 0xae, 0x27, 0x8c, 0x55, 0x7a, 0x88, 0x1f, 0x79, 0x19, 0xd3, 0xb8, 0x01, 0x6b, 0x85, 0xec, - 0xe2, 0xc7, 0xa4, 0x89, 0x76, 0xaa, 0x46, 0x50, 0xcd, 0x7a, 0x62, 0x00, 0x2e, 0xa4, 0x5d, 0x09, - 0x36, 0x10, 0xb2, 0x8f, 0x9f, 0xf8, 0x26, 0xa6, 0x67, 0x22, 0xad, 0x8e, 0x44, 0x00, 0x2e, 0x12, - 0xcc, 0xc6, 0x1a, 0xf0, 0xf7, 0x7e, 0xbe, 0x53, 0xe4, 0x0d, 0x0d, 0x02, 0xb0, 0xe5, 0xa8, 0xfd, - 0x90, 0x6a, 0x9a, 0x6d, 0x94, 0x62, 0x9c, 0x0a, 0x43, 0xb6, 0xbd, 0x78, 0x1a, 0xac, 0xce, 0x66, - 0xec, 0x3c, 0xf8, 0x94, 0x1c, 0xa0, 0xd6, 0x95, 0xb6, 0xa8, 0x5c, 0xfb, 0x63, 0xd5, 0x81, 0x92, - 0x9c, 0x57, 0x64, 0xf0, 0x4f, 0xbe, 0xa4, 0xe2, 0x68, 0x71, 0xc3, 0x00, 0x74, 0xe9, 0x7e, 0xfc, - 0xcc, 0x9b, 0xe2, 0xc2, 0xfb, 0xce, 0x11, 0x9e, 0xfb, 0x14, 0xc5, 0x2a, 0xaa, 0x65, 0xfc, 0x5c, - 0x5a, 0xc3, 0xea, 0xd8, 0x58, 0xe0, 0x2e, 0x36, 0xa0, 0xf1, 0x2f, 0x65, 0xc7, 0xa7, 0xd9, 0x65, - 0x7d, 0xbf, 0x96, 0x1d, 0xbf, 0x50, 0xb9, 0xe3, 0xc0, 0x84, 0xf1, 0x89, 0x7f, 0xcb, 0x76, 0x50, - 0x8d, 0x04, 0x01, 0xd0, 0x01, 0xe0, 0xdf, 0x3d, 0x9e, 0xa6, 0xc8, 0x9d, 0xee, 0xb7, 0x6e, 0x58, - 0x19, 0xfe, 0x8f, 0xb2, 0xf5, 0x86, 0x0e, 0x80, 0x17, 0xcb, 0x19, 0xbf, 0xf0, 0xdb, 0xa4, 0xca, - 0xcb, 0xa8, 0x64, 0x10, 0x5c, 0x1a, 0xbc, 0x3f, 0xbd, 0x32, 0x39, 0x56, 0x5b, 0xf7, 0xcb, 0xce, - 0xd2, 0x5f, 0x0b, 0xed, 0xa7, 0x2f, 0x8a, 0xdf, 0xc3, 0xb7, 0x73, 0xe9, 0x7f, 0xcf, 0xff, 0x0f, - 0x00, 0x00, 0xff, 0xff, 0x84, 0x77, 0xdc, 0x48, 0xb6, 0x07, 0x00, 0x00, + // 928 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x55, 0x6b, 0x73, 0x13, 0x37, + 0x14, 0x6d, 0x20, 0x4d, 0x40, 0x79, 0x29, 0x22, 0x0f, 0xe7, 0x6d, 0x0c, 0x0d, 0x01, 0x5a, 0xd3, + 0x42, 0xdb, 0x69, 0x4b, 0x69, 0x47, 0x96, 0x6e, 0x6c, 0xe1, 0x5d, 0x69, 0x91, 0xb4, 0x66, 0xdc, + 0x2f, 0x9a, 0xa5, 0xb8, 0x4c, 0x66, 0x80, 0x78, 0x88, 0xf9, 0x90, 0x7f, 0xd0, 0x5f, 0xd1, 0xdf, + 0xda, 0xd1, 0x3e, 0x9d, 0xd8, 0x21, 0x9f, 0x92, 0xbd, 0xf7, 0xe8, 0x4a, 0xe7, 0xdc, 0x73, 0xaf, + 0x51, 0x23, 0x19, 0x0e, 0xdf, 0x9f, 0xfc, 0x9d, 0x8c, 0x4e, 0x4e, 0x3f, 0xba, 0x0f, 0x83, 0x51, + 0xf2, 0x36, 0x19, 0x25, 0xee, 0xc3, 0xe0, 0xec, 0x2c, 0x79, 0x37, 0x68, 0x0e, 0x3f, 0x9d, 0x8e, + 0x4e, 0xc9, 0xad, 0xf4, 0xcf, 0x9b, 0xcf, 0xff, 0x34, 0xfe, 0x5b, 0x41, 0xdb, 0xb4, 0x3a, 0x10, + 0xe6, 0xf8, 0x30, 0x83, 0x93, 0x5d, 0x74, 0xfb, 0xec, 0xe4, 0xdd, 0xc7, 0x64, 0xf4, 0xf9, 0xd3, + 0xa0, 0x36, 0x53, 0x9f, 0x39, 0x5a, 0xd4, 0x55, 0x80, 0xd4, 0xd0, 0xfc, 0x30, 0x39, 0x7f, 0x7f, + 0x9a, 0xbc, 0xad, 0xdd, 0x48, 0x73, 0xc5, 0x27, 0x79, 0x81, 0x66, 0x47, 0xe7, 0xc3, 0x41, 0xed, + 0x66, 0x7d, 0xe6, 0x68, 0xf9, 0xe9, 0xc3, 0x66, 0x71, 0x5f, 0xf3, 0xea, 0xbb, 0x9a, 0xf6, 0x7c, + 0x38, 0xd0, 0xe9, 0xb1, 0xc6, 0xbf, 0xcb, 0x68, 0xd6, 0x7f, 0x92, 0x05, 0x34, 0x1f, 0xcb, 0xae, + 0x54, 0xaf, 0x25, 0xfe, 0x8a, 0x60, 0xb4, 0xc8, 0x3a, 0xd4, 0xba, 0x10, 0x8c, 0xa1, 0x6d, 0xc0, + 0x33, 0x84, 0xa0, 0x65, 0xa6, 0xa4, 0xa5, 0xcc, 0xba, 0x38, 0xe2, 0xd4, 0x02, 0xbe, 0x41, 0xf6, + 0xd0, 0x56, 0x08, 0x61, 0x0b, 0xb4, 0xe9, 0x88, 0x28, 0x0f, 0x97, 0x47, 0x6e, 0x92, 0x75, 0xb4, + 0x1a, 0x51, 0xa1, 0x9d, 0x90, 0xc6, 0xd2, 0x20, 0xa0, 0x56, 0x28, 0x89, 0x67, 0x7d, 0xd8, 0xf4, + 0x25, 0xbb, 0x18, 0xfe, 0x9a, 0xdc, 0x43, 0x07, 0x1a, 0x5e, 0xc5, 0x60, 0xac, 0xa3, 0x9c, 0x6b, + 0x30, 0xc6, 0x1d, 0x2b, 0xed, 0xac, 0xa6, 0xd2, 0x50, 0x96, 0x82, 0xe6, 0xc8, 0x23, 0x74, 0x48, + 0x19, 0x83, 0xc8, 0xba, 0xeb, 0xb0, 0xf3, 0xe4, 0x31, 0x7a, 0xc0, 0x81, 0x05, 0x42, 0xc2, 0xb5, + 0xe0, 0x5b, 0x64, 0x13, 0xdd, 0x29, 0x40, 0xe3, 0x89, 0xdb, 0x64, 0x0d, 0x61, 0x03, 0x92, 0x5f, + 0x88, 0x22, 0x72, 0x80, 0x76, 0x2e, 0xd7, 0x1e, 0x07, 0x2c, 0x78, 0x69, 0x26, 0x48, 0xba, 0x5c, + 0x40, 0xbc, 0x38, 0x3d, 0x4d, 0x19, 0x53, 0xb1, 0xb4, 0x78, 0x89, 0xdc, 0x45, 0x7b, 0x93, 0xe9, + 0x28, 0x6e, 0x05, 0x82, 0x39, 0xdf, 0x17, 0xbc, 0x4c, 0xf6, 0xd1, 0x76, 0xd1, 0x0f, 0xa6, 0x38, + 0x38, 0xca, 0x7b, 0xa0, 0xad, 0x30, 0x10, 0x82, 0xb4, 0x78, 0x85, 0x34, 0xd0, 0x7e, 0x14, 0x9b, + 0x8e, 0x93, 0xca, 0x8a, 0x63, 0xc1, 0xb2, 0x12, 0x1a, 0xda, 0xc2, 0x58, 0x9d, 0x49, 0x8e, 0xbd, + 0x42, 0x5f, 0xc6, 0x38, 0x0d, 0x26, 0x52, 0xd2, 0x00, 0x5e, 0x25, 0x3b, 0x68, 0x73, 0x12, 0xfc, + 0x2a, 0x06, 0xdd, 0xc7, 0x84, 0xdc, 0x47, 0xf5, 0x2b, 0x92, 0x55, 0x89, 0x3b, 0x9e, 0xf5, 0xb4, + 0xfb, 0x52, 0xfd, 0xf0, 0x9a, 0xa7, 0x34, 0x2d, 0x9d, 0x1f, 0x5f, 0xf7, 0x16, 0x84, 0x50, 0xbd, + 0x14, 0x4e, 0x43, 0xae, 0xf3, 0x06, 0xd9, 0x42, 0xeb, 0x6d, 0xad, 0xe2, 0x28, 0x95, 0xc5, 0x09, + 0xd9, 0x13, 0x36, 0x63, 0xb7, 0x49, 0x56, 0xd1, 0x52, 0x16, 0xe4, 0x20, 0xad, 0xb0, 0x7d, 0x5c, + 0xf3, 0x68, 0xa6, 0xc2, 0x30, 0x96, 0xc2, 0xf6, 0x1d, 0x07, 0xc3, 0xb4, 0x88, 0x52, 0xf4, 0x16, + 0xa9, 0xa1, 0xb5, 0x2a, 0x35, 0x56, 0x67, 0xdb, 0xbf, 0xba, 0xca, 0x94, 0xdd, 0x56, 0xee, 0xa5, + 0x12, 0x12, 0xef, 0x90, 0x15, 0xb4, 0x10, 0x09, 0x59, 0xda, 0x7e, 0xd7, 0xcf, 0x0e, 0x70, 0x51, + 0xcd, 0xce, 0x9e, 0x7f, 0x89, 0xb1, 0xd4, 0xc6, 0xa6, 0x18, 0x9d, 0x7d, 0xcf, 0x85, 0x43, 0x00, + 0x63, 0xf3, 0x72, 0xe0, 0x4d, 0x35, 0xcd, 0x33, 0xf9, 0xd5, 0xb8, 0x4e, 0xb6, 0xd1, 0x06, 0x95, + 0x4a, 0xf6, 0x43, 0x15, 0x1b, 0x17, 0x82, 0xd5, 0x82, 0xb9, 0x16, 0xb5, 0xac, 0x83, 0xef, 0x96, + 0x53, 0x95, 0x52, 0xd6, 0x10, 0xaa, 0x1e, 0x70, 0xdc, 0xf0, 0x5d, 0xab, 0xc2, 0xf9, 0x55, 0xc6, + 0x0b, 0xc8, 0xf1, 0x3d, 0x82, 0xd0, 0x5c, 0x8b, 0xb2, 0x6e, 0x1c, 0xe1, 0xfb, 0xa5, 0x23, 0xbd, + 0xb2, 0x3d, 0xcf, 0x94, 0x81, 0xb4, 0xa0, 0x33, 0xe8, 0x37, 0xa5, 0x23, 0x2f, 0xa7, 0xb3, 0x69, + 0x04, 0x8e, 0x0f, 0xbd, 0xe3, 0xa6, 0x42, 0xb8, 0x30, 0xa1, 0x30, 0x06, 0x38, 0x7e, 0x90, 0x2a, + 0xe1, 0x31, 0x2d, 0xa5, 0xba, 0x21, 0xd5, 0x5d, 0x7c, 0x44, 0x36, 0x10, 0xc9, 0x5e, 0x18, 0x00, + 0xd5, 0xae, 0x23, 0x8c, 0x55, 0xba, 0x8f, 0x1f, 0x7a, 0x19, 0xd3, 0xb8, 0x01, 0x6b, 0x85, 0x6c, + 0xe3, 0x47, 0xa4, 0x8e, 0x76, 0xab, 0x46, 0x50, 0xcd, 0x3a, 0xa2, 0x07, 0x2e, 0xa4, 0x6d, 0x09, + 0x36, 0x10, 0xb2, 0x8b, 0x1f, 0xfb, 0x26, 0xa6, 0x67, 0x22, 0xad, 0x8e, 0x45, 0x00, 0x2e, 0x12, + 0xcc, 0xc6, 0x1a, 0xf0, 0xb7, 0x7e, 0xbe, 0xd3, 0xcc, 0x6b, 0x1a, 0x04, 0x60, 0xcb, 0x51, 0xfb, + 0x2e, 0xd5, 0x34, 0xdb, 0x28, 0xc5, 0x38, 0x15, 0x86, 0x6c, 0x7a, 0xf1, 0x34, 0x58, 0x9d, 0xcd, + 0xd8, 0xc5, 0xe4, 0x13, 0x72, 0x88, 0x1a, 0x57, 0xda, 0xa2, 0x72, 0xed, 0xf7, 0x55, 0x07, 0x4a, + 0x70, 0xce, 0xc8, 0xe0, 0x1f, 0x3c, 0xa5, 0xe2, 0x68, 0x71, 0x43, 0x0f, 0x74, 0xe9, 0x7e, 0xfc, + 0xd4, 0x9b, 0xe2, 0xd2, 0xfb, 0x2e, 0x00, 0x9e, 0xf9, 0x12, 0xc5, 0x2a, 0x9a, 0x8a, 0xf8, 0xb1, + 0xb4, 0x86, 0xd5, 0xb1, 0xb1, 0xc0, 0x5d, 0x6c, 0x40, 0xe3, 0x9f, 0xca, 0x8e, 0x8f, 0xa3, 0x4b, + 0x7e, 0x3f, 0x97, 0x1d, 0xbf, 0xc4, 0xdc, 0x71, 0x60, 0xc2, 0xf8, 0xc2, 0xbf, 0x64, 0x3b, 0x68, + 0x8a, 0x04, 0x01, 0xd0, 0x1e, 0xe0, 0x5f, 0x7d, 0x3e, 0x2d, 0x91, 0x3b, 0xdd, 0x6f, 0xdd, 0xb0, + 0x32, 0xfc, 0x6f, 0x65, 0xeb, 0x0d, 0xed, 0x01, 0x2f, 0x96, 0x33, 0x7e, 0xee, 0xb7, 0x49, 0x55, + 0x97, 0x51, 0xc9, 0x20, 0x98, 0x18, 0xbc, 0xdf, 0xbd, 0x32, 0x79, 0x6e, 0x2a, 0xef, 0x17, 0x25, + 0x6f, 0x1a, 0x04, 0xae, 0x0b, 0x7d, 0x46, 0x35, 0x37, 0xf8, 0x8f, 0xd2, 0x0a, 0x79, 0xc8, 0xe5, + 0xbb, 0xe4, 0xcf, 0xd6, 0xd2, 0x5f, 0x0b, 0xcd, 0x27, 0xcf, 0x8b, 0xdf, 0xcf, 0x37, 0x73, 0xe9, + 0x7f, 0xcf, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x4d, 0xc7, 0x99, 0x73, 0xe6, 0x07, 0x00, 0x00, } diff --git a/protocol/protobuf/application_metadata_message.proto b/protocol/protobuf/application_metadata_message.proto index 2ce8bed6e..4745b01df 100644 --- a/protocol/protobuf/application_metadata_message.proto +++ b/protocol/protobuf/application_metadata_message.proto @@ -74,5 +74,7 @@ message ApplicationMetadataMessage { SYNC_SAVED_ADDRESS = 59; COMMUNITY_CANCEL_REQUEST_TO_JOIN = 60; CANCEL_CONTACT_VERIFICATION = 61; + SYNC_ALL_KEYCARDS = 62; + SYNC_KEYCARD_ACTION = 63; } } diff --git a/protocol/protobuf/pairing.pb.go b/protocol/protobuf/pairing.pb.go index a44250778..617da03c1 100644 --- a/protocol/protobuf/pairing.pb.go +++ b/protocol/protobuf/pairing.pb.go @@ -107,6 +107,49 @@ func (SyncContactRequestDecision_DecisionStatus) EnumDescriptor() ([]byte, []int return fileDescriptor_d61ab7221f0b5518, []int{27, 0} } +type SyncKeycardAction_Action int32 + +const ( + SyncKeycardAction_KEYCARD_ADDED SyncKeycardAction_Action = 0 + SyncKeycardAction_ACCOUNTS_ADDED SyncKeycardAction_Action = 1 + SyncKeycardAction_KEYCARD_DELETED SyncKeycardAction_Action = 2 + SyncKeycardAction_ACCOUNTS_REMOVED SyncKeycardAction_Action = 3 + SyncKeycardAction_LOCKED SyncKeycardAction_Action = 4 + SyncKeycardAction_UNLOCKED SyncKeycardAction_Action = 5 + SyncKeycardAction_UID_UPDATED SyncKeycardAction_Action = 6 + SyncKeycardAction_NAME_CHANGED SyncKeycardAction_Action = 7 +) + +var SyncKeycardAction_Action_name = map[int32]string{ + 0: "KEYCARD_ADDED", + 1: "ACCOUNTS_ADDED", + 2: "KEYCARD_DELETED", + 3: "ACCOUNTS_REMOVED", + 4: "LOCKED", + 5: "UNLOCKED", + 6: "UID_UPDATED", + 7: "NAME_CHANGED", +} + +var SyncKeycardAction_Action_value = map[string]int32{ + "KEYCARD_ADDED": 0, + "ACCOUNTS_ADDED": 1, + "KEYCARD_DELETED": 2, + "ACCOUNTS_REMOVED": 3, + "LOCKED": 4, + "UNLOCKED": 5, + "UID_UPDATED": 6, + "NAME_CHANGED": 7, +} + +func (x SyncKeycardAction_Action) String() string { + return proto.EnumName(SyncKeycardAction_Action_name, int32(x)) +} + +func (SyncKeycardAction_Action) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_d61ab7221f0b5518, []int{32, 0} +} + // `FetchingBackedUpDataDetails` is used to describe how many messages a single backup data structure consists of type FetchingBackedUpDataDetails struct { DataNumber uint32 `protobuf:"varint,1,opt,name=data_number,json=dataNumber,proto3" json:"data_number,omitempty"` @@ -2486,10 +2529,192 @@ func (m *SyncRawMessage) GetSettingsJsonBytes() []byte { return nil } +type SyncKeycard struct { + Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Locked bool `protobuf:"varint,3,opt,name=locked,proto3" json:"locked,omitempty"` + KeyUid string `protobuf:"bytes,4,opt,name=key_uid,json=keyUid,proto3" json:"key_uid,omitempty"` + Addresses [][]byte `protobuf:"bytes,5,rep,name=addresses,proto3" json:"addresses,omitempty"` + Clock uint64 `protobuf:"varint,6,opt,name=clock,proto3" json:"clock,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SyncKeycard) Reset() { *m = SyncKeycard{} } +func (m *SyncKeycard) String() string { return proto.CompactTextString(m) } +func (*SyncKeycard) ProtoMessage() {} +func (*SyncKeycard) Descriptor() ([]byte, []int) { + return fileDescriptor_d61ab7221f0b5518, []int{31} +} + +func (m *SyncKeycard) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SyncKeycard.Unmarshal(m, b) +} +func (m *SyncKeycard) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SyncKeycard.Marshal(b, m, deterministic) +} +func (m *SyncKeycard) XXX_Merge(src proto.Message) { + xxx_messageInfo_SyncKeycard.Merge(m, src) +} +func (m *SyncKeycard) XXX_Size() int { + return xxx_messageInfo_SyncKeycard.Size(m) +} +func (m *SyncKeycard) XXX_DiscardUnknown() { + xxx_messageInfo_SyncKeycard.DiscardUnknown(m) +} + +var xxx_messageInfo_SyncKeycard proto.InternalMessageInfo + +func (m *SyncKeycard) GetUid() string { + if m != nil { + return m.Uid + } + return "" +} + +func (m *SyncKeycard) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *SyncKeycard) GetLocked() bool { + if m != nil { + return m.Locked + } + return false +} + +func (m *SyncKeycard) GetKeyUid() string { + if m != nil { + return m.KeyUid + } + return "" +} + +func (m *SyncKeycard) GetAddresses() [][]byte { + if m != nil { + return m.Addresses + } + return nil +} + +func (m *SyncKeycard) GetClock() uint64 { + if m != nil { + return m.Clock + } + return 0 +} + +type SyncKeycardAction struct { + Action SyncKeycardAction_Action `protobuf:"varint,1,opt,name=action,proto3,enum=protobuf.SyncKeycardAction_Action" json:"action,omitempty"` + OldKeycardUid string `protobuf:"bytes,2,opt,name=oldKeycardUid,proto3" json:"oldKeycardUid,omitempty"` + Keycard *SyncKeycard `protobuf:"bytes,3,opt,name=keycard,proto3" json:"keycard,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SyncKeycardAction) Reset() { *m = SyncKeycardAction{} } +func (m *SyncKeycardAction) String() string { return proto.CompactTextString(m) } +func (*SyncKeycardAction) ProtoMessage() {} +func (*SyncKeycardAction) Descriptor() ([]byte, []int) { + return fileDescriptor_d61ab7221f0b5518, []int{32} +} + +func (m *SyncKeycardAction) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SyncKeycardAction.Unmarshal(m, b) +} +func (m *SyncKeycardAction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SyncKeycardAction.Marshal(b, m, deterministic) +} +func (m *SyncKeycardAction) XXX_Merge(src proto.Message) { + xxx_messageInfo_SyncKeycardAction.Merge(m, src) +} +func (m *SyncKeycardAction) XXX_Size() int { + return xxx_messageInfo_SyncKeycardAction.Size(m) +} +func (m *SyncKeycardAction) XXX_DiscardUnknown() { + xxx_messageInfo_SyncKeycardAction.DiscardUnknown(m) +} + +var xxx_messageInfo_SyncKeycardAction proto.InternalMessageInfo + +func (m *SyncKeycardAction) GetAction() SyncKeycardAction_Action { + if m != nil { + return m.Action + } + return SyncKeycardAction_KEYCARD_ADDED +} + +func (m *SyncKeycardAction) GetOldKeycardUid() string { + if m != nil { + return m.OldKeycardUid + } + return "" +} + +func (m *SyncKeycardAction) GetKeycard() *SyncKeycard { + if m != nil { + return m.Keycard + } + return nil +} + +type SyncAllKeycards struct { + Keycards []*SyncKeycard `protobuf:"bytes,1,rep,name=keycards,proto3" json:"keycards,omitempty"` + Clock uint64 `protobuf:"varint,2,opt,name=clock,proto3" json:"clock,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SyncAllKeycards) Reset() { *m = SyncAllKeycards{} } +func (m *SyncAllKeycards) String() string { return proto.CompactTextString(m) } +func (*SyncAllKeycards) ProtoMessage() {} +func (*SyncAllKeycards) Descriptor() ([]byte, []int) { + return fileDescriptor_d61ab7221f0b5518, []int{33} +} + +func (m *SyncAllKeycards) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SyncAllKeycards.Unmarshal(m, b) +} +func (m *SyncAllKeycards) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SyncAllKeycards.Marshal(b, m, deterministic) +} +func (m *SyncAllKeycards) XXX_Merge(src proto.Message) { + xxx_messageInfo_SyncAllKeycards.Merge(m, src) +} +func (m *SyncAllKeycards) XXX_Size() int { + return xxx_messageInfo_SyncAllKeycards.Size(m) +} +func (m *SyncAllKeycards) XXX_DiscardUnknown() { + xxx_messageInfo_SyncAllKeycards.DiscardUnknown(m) +} + +var xxx_messageInfo_SyncAllKeycards proto.InternalMessageInfo + +func (m *SyncAllKeycards) GetKeycards() []*SyncKeycard { + if m != nil { + return m.Keycards + } + return nil +} + +func (m *SyncAllKeycards) GetClock() uint64 { + if m != nil { + return m.Clock + } + return 0 +} + func init() { proto.RegisterEnum("protobuf.SyncTrustedUser_TrustStatus", SyncTrustedUser_TrustStatus_name, SyncTrustedUser_TrustStatus_value) proto.RegisterEnum("protobuf.SyncVerificationRequest_VerificationStatus", SyncVerificationRequest_VerificationStatus_name, SyncVerificationRequest_VerificationStatus_value) proto.RegisterEnum("protobuf.SyncContactRequestDecision_DecisionStatus", SyncContactRequestDecision_DecisionStatus_name, SyncContactRequestDecision_DecisionStatus_value) + proto.RegisterEnum("protobuf.SyncKeycardAction_Action", SyncKeycardAction_Action_name, SyncKeycardAction_Action_value) proto.RegisterType((*FetchingBackedUpDataDetails)(nil), "protobuf.FetchingBackedUpDataDetails") proto.RegisterType((*Backup)(nil), "protobuf.Backup") proto.RegisterType((*MultiAccount)(nil), "protobuf.MultiAccount") @@ -2524,6 +2749,9 @@ func init() { proto.RegisterType((*BackedUpProfile)(nil), "protobuf.BackedUpProfile") proto.RegisterType((*RawMessage)(nil), "protobuf.RawMessage") proto.RegisterType((*SyncRawMessage)(nil), "protobuf.SyncRawMessage") + proto.RegisterType((*SyncKeycard)(nil), "protobuf.SyncKeycard") + proto.RegisterType((*SyncKeycardAction)(nil), "protobuf.SyncKeycardAction") + proto.RegisterType((*SyncAllKeycards)(nil), "protobuf.SyncAllKeycards") } func init() { @@ -2531,153 +2759,168 @@ func init() { } var fileDescriptor_d61ab7221f0b5518 = []byte{ - // 2361 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x19, 0x4d, 0x73, 0x1b, 0x49, - 0x75, 0x47, 0x52, 0x2c, 0xe9, 0x49, 0x96, 0x95, 0x8e, 0x77, 0xa3, 0xd8, 0x4e, 0xc5, 0x99, 0x90, - 0xda, 0x50, 0x15, 0xbc, 0x54, 0x02, 0x2c, 0x6c, 0x36, 0xb5, 0xab, 0xc8, 0x66, 0xe3, 0x7c, 0x38, - 0xae, 0xb6, 0x9d, 0x00, 0x45, 0xd5, 0x54, 0x7b, 0xa6, 0x63, 0x35, 0x1e, 0xcd, 0x0c, 0xd3, 0x2d, - 0x87, 0xe1, 0xc6, 0x85, 0x1f, 0xc0, 0x85, 0xeb, 0x9e, 0xe1, 0x46, 0xd5, 0x72, 0xe2, 0x07, 0x70, - 0xe3, 0xc0, 0x85, 0x2a, 0xf8, 0x05, 0xfc, 0x0a, 0xaa, 0x5f, 0xf7, 0x48, 0x33, 0xb2, 0x64, 0x9c, - 0xe2, 0xc4, 0xc9, 0xfd, 0xde, 0xbc, 0xf7, 0xfa, 0xf5, 0xfb, 0x7e, 0x32, 0x2c, 0x27, 0x4c, 0xa4, - 0x22, 0x3a, 0xd9, 0x4a, 0xd2, 0x58, 0xc5, 0xa4, 0x81, 0x7f, 0x8e, 0xc7, 0x6f, 0xd7, 0xae, 0xc9, - 0x2c, 0xf2, 0x3d, 0xc9, 0x95, 0x12, 0xd1, 0x89, 0x34, 0x9f, 0xd7, 0x5c, 0x96, 0x24, 0xa1, 0xf0, - 0x99, 0x12, 0x71, 0xe4, 0x8d, 0xb8, 0x62, 0x01, 0x53, 0xcc, 0x1b, 0x71, 0x29, 0xd9, 0x09, 0x37, - 0x34, 0x2e, 0x83, 0xf5, 0x1f, 0x73, 0xe5, 0x0f, 0x45, 0x74, 0xf2, 0x84, 0xf9, 0xa7, 0x3c, 0x38, - 0x4a, 0xb6, 0x99, 0x62, 0xdb, 0x5c, 0x31, 0x11, 0x4a, 0x72, 0x0b, 0x5a, 0xc8, 0x14, 0x8d, 0x47, - 0xc7, 0x3c, 0xed, 0x39, 0x9b, 0xce, 0xbd, 0x65, 0x0a, 0x1a, 0xb5, 0x87, 0x18, 0x72, 0x1b, 0xda, - 0x2a, 0x56, 0x2c, 0xcc, 0x29, 0x2a, 0x48, 0xd1, 0x42, 0x9c, 0x21, 0x71, 0xff, 0x51, 0x83, 0x25, - 0x2d, 0x7b, 0x9c, 0x90, 0x55, 0xb8, 0xe2, 0x87, 0xb1, 0x7f, 0x8a, 0x82, 0x6a, 0xd4, 0x00, 0xa4, - 0x03, 0x15, 0x11, 0x20, 0x67, 0x93, 0x56, 0x44, 0x40, 0xbe, 0x80, 0x86, 0x1f, 0x47, 0x8a, 0xf9, - 0x4a, 0xf6, 0xaa, 0x9b, 0xd5, 0x7b, 0xad, 0x07, 0x77, 0xb6, 0xf2, 0x97, 0x6e, 0x1d, 0x64, 0x91, - 0xbf, 0x1b, 0x49, 0xc5, 0xc2, 0x10, 0x1f, 0x36, 0x30, 0x94, 0xaf, 0x1f, 0xd0, 0x09, 0x13, 0xf9, - 0x11, 0xb4, 0xfc, 0x78, 0x34, 0x1a, 0x47, 0x42, 0x09, 0x2e, 0x7b, 0x35, 0x94, 0x71, 0xbd, 0x2c, - 0x63, 0x60, 0x09, 0x32, 0x5a, 0xa4, 0x25, 0xaf, 0x60, 0x25, 0x17, 0x63, 0x6d, 0xd0, 0xbb, 0xb2, - 0xe9, 0xdc, 0x6b, 0x3d, 0xb8, 0x3b, 0x65, 0xbf, 0xc0, 0x60, 0x74, 0x96, 0x9b, 0x1c, 0x01, 0x29, - 0xc8, 0xcf, 0x65, 0x2e, 0xbd, 0x8f, 0xcc, 0x39, 0x02, 0xc8, 0x43, 0xa8, 0x27, 0x69, 0xfc, 0x56, - 0x84, 0xbc, 0x57, 0x47, 0x59, 0x37, 0xa6, 0xb2, 0x72, 0x19, 0xfb, 0x86, 0x80, 0xe6, 0x94, 0xe4, - 0x25, 0x74, 0xec, 0x31, 0xd7, 0xa3, 0xf1, 0x3e, 0x7a, 0xcc, 0x30, 0x93, 0x4f, 0xa0, 0x6e, 0x23, - 0xae, 0xd7, 0x44, 0x39, 0x1f, 0x96, 0x4d, 0x7c, 0x60, 0x3e, 0xd2, 0x9c, 0x4a, 0x1b, 0x37, 0x0f, - 0xd1, 0x5c, 0x01, 0x78, 0x2f, 0xe3, 0xce, 0x70, 0xbb, 0x7f, 0xa9, 0x41, 0xfb, 0xe5, 0x38, 0x54, - 0xa2, 0xef, 0xfb, 0xf1, 0x38, 0x52, 0x84, 0x40, 0x2d, 0x62, 0x23, 0x8e, 0xf1, 0xd5, 0xa4, 0x78, - 0x26, 0x1b, 0xd0, 0x54, 0x62, 0xc4, 0xa5, 0x62, 0xa3, 0x04, 0xa3, 0xac, 0x4a, 0xa7, 0x08, 0xfd, - 0x55, 0x04, 0x3c, 0x52, 0xc2, 0x8f, 0xa3, 0x5e, 0x15, 0xd9, 0xa6, 0x08, 0xf2, 0x25, 0x80, 0x1f, - 0x87, 0x71, 0xea, 0x0d, 0x99, 0x1c, 0xda, 0x40, 0xba, 0x3d, 0x55, 0xb6, 0x78, 0xf7, 0xd6, 0x20, - 0x0e, 0xe3, 0x71, 0xfa, 0x94, 0xc9, 0x21, 0x6d, 0x22, 0x93, 0x3e, 0x92, 0x1e, 0xd4, 0x11, 0xd8, - 0x0d, 0x30, 0x90, 0xaa, 0x34, 0x07, 0xc9, 0xc7, 0xb0, 0x72, 0xca, 0x33, 0x9f, 0xa5, 0x81, 0x67, - 0xd3, 0x1a, 0xc3, 0xa2, 0x49, 0x3b, 0x16, 0xbd, 0x6f, 0xb0, 0xe4, 0x3a, 0xd4, 0x4f, 0x79, 0xe6, - 0x8d, 0x45, 0x80, 0xbe, 0x6e, 0xd2, 0xa5, 0x53, 0x9e, 0x1d, 0x89, 0x80, 0x7c, 0x0e, 0x4b, 0x62, - 0xc4, 0x4e, 0xb8, 0xf6, 0xa3, 0xd6, 0xec, 0x5b, 0x0b, 0x34, 0xdb, 0xc5, 0xf7, 0xa8, 0x6c, 0x57, - 0x13, 0x53, 0xcb, 0xb3, 0xe6, 0x02, 0x4c, 0x55, 0xd6, 0xa9, 0x29, 0xa2, 0x80, 0xff, 0xaa, 0xe7, - 0x6c, 0x56, 0xef, 0x55, 0xa9, 0x01, 0xd6, 0xfe, 0xe9, 0xc0, 0x72, 0x89, 0xbb, 0xa8, 0x8c, 0x53, - 0x52, 0x26, 0x37, 0x7d, 0xa5, 0x60, 0xfa, 0x1e, 0xd4, 0x13, 0x96, 0x85, 0x31, 0x0b, 0xd0, 0xb4, - 0x6d, 0x9a, 0x83, 0xfa, 0xba, 0x77, 0x22, 0x50, 0xda, 0xa6, 0xda, 0x28, 0x06, 0x20, 0x1f, 0xc1, - 0xd2, 0x90, 0x8b, 0x93, 0xa1, 0xb2, 0xb6, 0xb2, 0x10, 0x59, 0x83, 0x86, 0x0e, 0x3c, 0x29, 0x7e, - 0xcd, 0xd1, 0x46, 0x55, 0x3a, 0x81, 0xc9, 0x1d, 0x58, 0x4e, 0xf1, 0xe4, 0x29, 0x96, 0x9e, 0x70, - 0x85, 0x36, 0xaa, 0xd2, 0xb6, 0x41, 0x1e, 0x22, 0x6e, 0x5a, 0x78, 0x1a, 0x85, 0xc2, 0xe3, 0xfe, - 0xdd, 0x81, 0x6b, 0x2f, 0x62, 0x9f, 0x85, 0xd6, 0xd2, 0xfb, 0x56, 0xb9, 0xef, 0x43, 0xed, 0x94, - 0x67, 0x12, 0x4d, 0x51, 0xf2, 0xf7, 0x1c, 0xe2, 0xad, 0xe7, 0x3c, 0xa3, 0x48, 0x4e, 0x3e, 0x83, - 0xf6, 0x48, 0x9b, 0x9d, 0x19, 0xb3, 0xa3, 0x25, 0x5a, 0x0f, 0x3e, 0x9a, 0xef, 0x14, 0x5a, 0xa2, - 0xd5, 0x2f, 0x4c, 0x98, 0x94, 0xef, 0xe2, 0x34, 0xb0, 0x51, 0x38, 0x81, 0xd7, 0xbe, 0x03, 0xd5, - 0xe7, 0x3c, 0x9b, 0x1b, 0xdb, 0x04, 0x6a, 0xba, 0x18, 0xe3, 0x55, 0x6d, 0x8a, 0x67, 0xf7, 0xb7, - 0x0e, 0x74, 0xb5, 0x8e, 0xc5, 0x2a, 0xb9, 0xa0, 0xf2, 0x7e, 0x0c, 0x2b, 0xa2, 0x40, 0xe5, 0x4d, - 0xca, 0x70, 0xa7, 0x88, 0xde, 0x0d, 0xb0, 0x0f, 0xf0, 0x33, 0xe1, 0x73, 0x4f, 0x65, 0x09, 0xb7, - 0x1a, 0x82, 0x41, 0x1d, 0x66, 0x09, 0x9f, 0x28, 0x57, 0x9b, 0x2a, 0xe7, 0xfe, 0xdb, 0x81, 0xeb, - 0x0b, 0xca, 0xf5, 0x25, 0x3b, 0xc1, 0x1d, 0x58, 0xb6, 0x35, 0xc7, 0xc3, 0xa0, 0xb5, 0x17, 0xb7, - 0x2d, 0xd2, 0x44, 0xe4, 0x0d, 0x68, 0xf0, 0x48, 0x7a, 0x85, 0xeb, 0xeb, 0x3c, 0x92, 0x7b, 0xda, - 0x3c, 0xb7, 0xa1, 0x1d, 0x32, 0xa9, 0xbc, 0x71, 0x12, 0x30, 0xc5, 0x4d, 0x06, 0xd6, 0x68, 0x4b, - 0xe3, 0x8e, 0x0c, 0x4a, 0xbf, 0x4c, 0x66, 0x52, 0xf1, 0x91, 0xa7, 0xd8, 0x89, 0x2e, 0xcc, 0x55, - 0xfd, 0x32, 0x83, 0x3a, 0x64, 0x27, 0x92, 0xdc, 0x85, 0x4e, 0xa8, 0xdd, 0xee, 0x45, 0xc2, 0x3f, - 0xc5, 0x4b, 0x4c, 0x12, 0x2e, 0x23, 0x76, 0xcf, 0x22, 0xdd, 0xdf, 0x2c, 0xc1, 0x8d, 0x85, 0xbd, - 0x89, 0x7c, 0x17, 0x56, 0x8b, 0x8a, 0x78, 0xc8, 0x1b, 0x66, 0xf6, 0xf5, 0xa4, 0xa0, 0xd0, 0x0b, - 0xf3, 0xe5, 0xff, 0xd8, 0x14, 0xda, 0xb7, 0x2c, 0x08, 0x78, 0x80, 0x5d, 0xa1, 0x41, 0x0d, 0xa0, - 0x6b, 0xc1, 0xb1, 0x76, 0x32, 0x0f, 0xb0, 0xe8, 0x37, 0x68, 0x0e, 0x6a, 0xfa, 0xd1, 0x58, 0xeb, - 0xd4, 0x32, 0xf4, 0x08, 0x68, 0xfa, 0x94, 0x8f, 0xe2, 0x33, 0x1e, 0xf4, 0xda, 0x86, 0xde, 0x82, - 0x64, 0x13, 0xda, 0x43, 0x26, 0x3d, 0x14, 0xeb, 0x8d, 0x65, 0x6f, 0x19, 0x3f, 0xc3, 0x90, 0xc9, - 0xbe, 0x46, 0x1d, 0xe9, 0xce, 0x74, 0xed, 0x8c, 0xa7, 0xe2, 0x6d, 0x3e, 0xfc, 0x48, 0xc5, 0xd4, - 0x58, 0xf6, 0x3a, 0x58, 0x19, 0x48, 0xf1, 0xd3, 0x01, 0x7e, 0xc1, 0x31, 0x26, 0x1d, 0x4b, 0x95, - 0x53, 0xae, 0x20, 0x65, 0x0b, 0x71, 0x96, 0xe4, 0x31, 0xac, 0xdb, 0xde, 0xee, 0xa5, 0xfc, 0x97, - 0x63, 0x2e, 0x95, 0xf1, 0x22, 0xb2, 0xf0, 0x5e, 0x17, 0x39, 0x7a, 0x96, 0x84, 0x1a, 0x0a, 0x74, - 0xa6, 0xe6, 0xe7, 0x8b, 0xd9, 0x4d, 0x1a, 0x5c, 0x5d, 0xc8, 0x3e, 0xc0, 0xcc, 0xf8, 0x02, 0x36, - 0x66, 0xd9, 0xb5, 0x39, 0x14, 0xb7, 0xd7, 0x13, 0xe4, 0xbf, 0x51, 0xe6, 0xa7, 0x48, 0x61, 0xee, - 0x5f, 0x2c, 0xc0, 0x28, 0x70, 0x6d, 0xb1, 0x00, 0xa3, 0xc1, 0x6d, 0x68, 0x07, 0x42, 0x26, 0x21, - 0xcb, 0x4c, 0x7c, 0xad, 0xa2, 0xeb, 0x5b, 0x16, 0xa7, 0x63, 0xcc, 0x7d, 0x77, 0x3e, 0xdf, 0xf3, - 0xc6, 0x3c, 0x3f, 0xdf, 0xcf, 0x05, 0x75, 0x65, 0x4e, 0x50, 0xcf, 0x46, 0x6e, 0xf5, 0x5c, 0xe4, - 0xba, 0x4f, 0x60, 0x6d, 0xf6, 0xe2, 0xfd, 0xf1, 0x71, 0x28, 0xfc, 0xc1, 0x90, 0x5d, 0xb2, 0xd6, - 0xb8, 0xdf, 0x54, 0x61, 0xb9, 0x34, 0x18, 0xfe, 0x57, 0xbe, 0x36, 0x26, 0xe6, 0x2d, 0x68, 0x25, - 0xa9, 0x38, 0x63, 0x8a, 0x7b, 0xa7, 0x3c, 0xb3, 0x7d, 0x0e, 0x2c, 0x4a, 0xd7, 0xed, 0x4d, 0x5d, - 0x3b, 0xa5, 0x9f, 0x8a, 0x44, 0xeb, 0x85, 0x79, 0xd9, 0xa6, 0x45, 0x94, 0x6e, 0x7b, 0xbf, 0x88, - 0x45, 0x64, 0xb3, 0xb2, 0x41, 0x2d, 0xa4, 0x9b, 0x82, 0x89, 0x55, 0x1e, 0x60, 0xdb, 0x6b, 0xd0, - 0x09, 0x3c, 0x4d, 0x9a, 0x7a, 0x31, 0x69, 0x5e, 0x41, 0xd7, 0x7a, 0x57, 0x7a, 0x2a, 0xf6, 0xb4, - 0x1c, 0x3b, 0x1b, 0xdc, 0x5d, 0x34, 0xfe, 0x5a, 0xf2, 0xc3, 0xf8, 0x59, 0x2c, 0x22, 0xda, 0x49, - 0x4b, 0x30, 0x79, 0x04, 0x8d, 0x7c, 0xe8, 0xb2, 0x43, 0xde, 0xad, 0x05, 0x82, 0xec, 0xb4, 0x27, - 0xe9, 0x84, 0x41, 0xcf, 0x56, 0x3c, 0xf2, 0xd3, 0x2c, 0x51, 0x93, 0xa4, 0x9f, 0x22, 0xf4, 0x57, - 0x99, 0x70, 0x5f, 0xb1, 0x69, 0xea, 0x4f, 0x11, 0xba, 0x35, 0x59, 0x52, 0x9d, 0xc0, 0xd8, 0x8e, - 0xdb, 0x68, 0xb9, 0xce, 0x14, 0xfd, 0x9c, 0x67, 0xd2, 0xfd, 0x9b, 0x03, 0xeb, 0x17, 0xbc, 0xc8, - 0xfa, 0xcb, 0x99, 0xf8, 0xeb, 0x26, 0x40, 0x82, 0xb1, 0x81, 0xee, 0x32, 0xfe, 0x6f, 0x1a, 0x8c, - 0xf6, 0xd6, 0xc4, 0xe9, 0xd5, 0xa2, 0xd3, 0x2f, 0x28, 0xac, 0xd7, 0xa1, 0xee, 0x0f, 0x99, 0xd2, - 0xbd, 0xf3, 0x8a, 0x19, 0x88, 0x34, 0xb8, 0x1b, 0xe8, 0xb8, 0xcd, 0x07, 0xf7, 0x4c, 0x7f, 0x5d, - 0x32, 0x8e, 0x9f, 0xe0, 0x76, 0xd1, 0x89, 0x26, 0x7d, 0xeb, 0xe6, 0x32, 0x04, 0xdc, 0xdf, 0x55, - 0xa0, 0x3b, 0x1b, 0xce, 0xe4, 0x71, 0x61, 0x29, 0x3a, 0x37, 0x97, 0x2c, 0x68, 0x3c, 0x85, 0x95, - 0xe8, 0x2b, 0x68, 0xdb, 0x57, 0x6b, 0xed, 0x64, 0xaf, 0x32, 0x3b, 0x30, 0x2e, 0xce, 0x1f, 0xda, - 0x4a, 0x26, 0x67, 0x49, 0x1e, 0x41, 0x3d, 0x9f, 0x6f, 0xaa, 0x18, 0x0f, 0x17, 0xa8, 0x91, 0x8f, - 0x3a, 0x39, 0xc7, 0xff, 0xb0, 0x98, 0xb9, 0x9f, 0xc2, 0x0a, 0x7e, 0xd5, 0x0a, 0xd9, 0x3e, 0x70, - 0xb9, 0xbc, 0xfe, 0x1c, 0x56, 0x73, 0xc6, 0x97, 0x66, 0xf5, 0x95, 0x94, 0xb3, 0xcb, 0x72, 0x7f, - 0x09, 0x1f, 0x69, 0xee, 0xbe, 0xaf, 0xc4, 0x99, 0x50, 0xd9, 0x80, 0x47, 0x8a, 0xa7, 0x17, 0xf0, - 0x77, 0xa1, 0x2a, 0x02, 0x63, 0xde, 0x36, 0xd5, 0x47, 0x77, 0xdb, 0xd4, 0xa6, 0xb2, 0x84, 0xbe, - 0xef, 0x73, 0x4c, 0x82, 0xcb, 0x4a, 0xd9, 0x31, 0x41, 0x5e, 0x96, 0xb2, 0x2d, 0xe4, 0x48, 0x48, - 0xf9, 0x1e, 0x62, 0xbe, 0x76, 0xa0, 0xad, 0xe5, 0x3c, 0x89, 0xe3, 0xd3, 0x11, 0x4b, 0x4f, 0x17, - 0x33, 0x8e, 0xd3, 0xd0, 0x9a, 0x41, 0x1f, 0x27, 0xf3, 0x5d, 0xb5, 0x30, 0x7c, 0xae, 0x43, 0x13, - 0xab, 0xb6, 0xa7, 0x69, 0x4d, 0x56, 0x34, 0x10, 0x71, 0x94, 0x86, 0xc5, 0xf6, 0x7d, 0xa5, 0xdc, - 0xbe, 0x6f, 0x02, 0x04, 0x3c, 0xe4, 0x7a, 0x0c, 0x62, 0x0a, 0xb3, 0xa2, 0x46, 0x9b, 0x16, 0xd3, - 0x57, 0xee, 0x33, 0x13, 0xfc, 0x83, 0x90, 0xb3, 0xf4, 0xa9, 0x90, 0x2a, 0x4e, 0xb3, 0x62, 0x8e, - 0x39, 0xa5, 0x1c, 0xbb, 0x09, 0xe0, 0x6b, 0x42, 0x23, 0xab, 0x62, 0x64, 0x59, 0x4c, 0x5f, 0xb9, - 0x7f, 0x75, 0x80, 0x68, 0x61, 0x76, 0x13, 0xde, 0x17, 0xbe, 0x1a, 0xa7, 0x7c, 0xee, 0x24, 0x5d, - 0x58, 0x55, 0x2a, 0x0b, 0x56, 0x95, 0x2a, 0xfe, 0xb6, 0x71, 0x6e, 0x55, 0xa9, 0x21, 0x3a, 0x5f, - 0x55, 0xd6, 0xa1, 0x89, 0xfd, 0x0c, 0x77, 0x95, 0x2b, 0xf8, 0x09, 0x77, 0x95, 0x83, 0xb9, 0xbb, - 0xca, 0x12, 0x12, 0x2c, 0xd8, 0x55, 0xea, 0xc5, 0x5d, 0x65, 0x08, 0xd7, 0xce, 0xbf, 0x44, 0x2e, - 0x5e, 0xc7, 0x7e, 0x08, 0x8d, 0xc4, 0x12, 0xd9, 0x64, 0xdf, 0x28, 0xe7, 0x59, 0x59, 0x12, 0x9d, - 0x50, 0xbb, 0x7f, 0xac, 0xc0, 0x55, 0x4d, 0xf0, 0x86, 0x85, 0x21, 0x57, 0x17, 0x37, 0xf0, 0x1e, - 0xd4, 0x59, 0x10, 0xa4, 0x5c, 0xca, 0xdc, 0x6a, 0x16, 0xd4, 0xf6, 0x79, 0x87, 0x02, 0xd0, 0x6c, - 0x0d, 0x6a, 0x21, 0x6d, 0x7b, 0xed, 0x3b, 0xb4, 0x5a, 0x83, 0xe2, 0x59, 0xe3, 0x70, 0xad, 0x30, - 0xf5, 0x13, 0xcf, 0x5a, 0xb2, 0xf6, 0xbd, 0x1e, 0x0a, 0xcc, 0x56, 0x9c, 0x83, 0x9a, 0x3a, 0x61, - 0x6a, 0x68, 0x67, 0x4f, 0x3c, 0xeb, 0x5e, 0x32, 0x29, 0xe1, 0xb8, 0xe3, 0xb5, 0x8b, 0x35, 0x3d, - 0xf7, 0x77, 0xb3, 0xe0, 0x6f, 0xfd, 0x1e, 0xbd, 0x88, 0x63, 0x5f, 0x6a, 0x52, 0x03, 0xa0, 0x57, - 0x45, 0x10, 0xf0, 0xc8, 0x36, 0x24, 0x0b, 0x2d, 0x1e, 0x46, 0xdd, 0x97, 0x26, 0xc2, 0x4a, 0xc6, - 0x92, 0xe4, 0x53, 0x68, 0xd8, 0x9a, 0x97, 0x57, 0xeb, 0xf5, 0xb2, 0xf5, 0x4b, 0xf4, 0x74, 0x42, - 0xec, 0xfe, 0xd9, 0x31, 0xe1, 0x7f, 0xc0, 0xce, 0x78, 0xd0, 0xb7, 0xb6, 0x2c, 0x58, 0xd9, 0x29, - 0x5b, 0x79, 0xde, 0xd2, 0xbd, 0x01, 0xcd, 0xb7, 0xec, 0x2c, 0x1e, 0xa7, 0x42, 0x71, 0x6b, 0xfc, - 0x29, 0x42, 0x77, 0x32, 0x7f, 0xc8, 0x04, 0xee, 0x7a, 0x35, 0x74, 0x65, 0x1d, 0xe1, 0xdd, 0xe0, - 0x82, 0x94, 0xbd, 0x0d, 0x6d, 0x33, 0x7d, 0x79, 0xc5, 0xc8, 0x6c, 0x19, 0x1c, 0x8e, 0x87, 0xee, - 0xef, 0x1d, 0xf8, 0x70, 0xee, 0x3c, 0xb0, 0x20, 0x72, 0x66, 0xbb, 0xa3, 0x79, 0x41, 0xa9, 0x3b, - 0xee, 0xc0, 0xad, 0xa1, 0x29, 0x00, 0x1e, 0x4b, 0xfd, 0xa1, 0x38, 0xe3, 0x9e, 0x1c, 0x27, 0x49, - 0x9c, 0x2a, 0x8f, 0x47, 0xec, 0x38, 0xb4, 0xb3, 0x60, 0x83, 0x6e, 0x58, 0xb2, 0xbe, 0xa1, 0x3a, - 0x30, 0x44, 0x3b, 0x86, 0xc6, 0xfd, 0x93, 0x63, 0x5a, 0xc7, 0xa1, 0x1e, 0xe6, 0xf5, 0x7a, 0xc0, - 0xd3, 0x4b, 0xae, 0x9f, 0x8f, 0x61, 0xc9, 0xee, 0x03, 0xfa, 0x9e, 0xce, 0xec, 0x0c, 0x55, 0x10, - 0xb8, 0x75, 0x38, 0xdd, 0x14, 0xa8, 0x65, 0x72, 0x3f, 0x83, 0x56, 0x01, 0x4d, 0x5a, 0x50, 0x3f, - 0xda, 0x7b, 0xbe, 0xf7, 0xea, 0xcd, 0x5e, 0xf7, 0x03, 0x0d, 0x1c, 0xd2, 0xa3, 0x83, 0xc3, 0x9d, - 0xed, 0xae, 0x43, 0xae, 0xc2, 0xf2, 0xd1, 0x1e, 0x82, 0x6f, 0x5e, 0xd1, 0xc3, 0xa7, 0x3f, 0xed, - 0x56, 0xdc, 0xaf, 0xab, 0x66, 0x96, 0x7e, 0x5d, 0xd8, 0x55, 0xec, 0x60, 0xb3, 0x40, 0x79, 0x02, - 0xb5, 0xb7, 0x69, 0x3c, 0xca, 0x43, 0x41, 0x9f, 0xf5, 0x83, 0x54, 0x6c, 0x6b, 0x76, 0x45, 0xc5, - 0x3a, 0x34, 0xfc, 0xa1, 0x8e, 0xbc, 0xe8, 0x24, 0x9f, 0x63, 0xa6, 0x08, 0xed, 0x12, 0x3b, 0xfd, - 0x99, 0x72, 0x6a, 0x57, 0xc4, 0x09, 0xae, 0x8f, 0x3f, 0x53, 0xa4, 0x5c, 0x26, 0x71, 0x24, 0xf3, - 0xb4, 0x9c, 0xc0, 0xba, 0x16, 0xa7, 0x3c, 0x09, 0x85, 0x61, 0x36, 0x21, 0xd2, 0xb4, 0x98, 0xbe, - 0x22, 0x7c, 0xfe, 0x4e, 0xd6, 0x40, 0xcb, 0x7e, 0xaf, 0x6c, 0xd9, 0x39, 0xaf, 0xde, 0x7a, 0x7d, - 0x6e, 0x6b, 0x9b, 0xbb, 0xc9, 0x19, 0x1f, 0x36, 0x27, 0x0d, 0xfc, 0x27, 0x40, 0xce, 0x73, 0x9e, - 0xf3, 0xc5, 0xfe, 0xce, 0xde, 0xf6, 0xee, 0xde, 0x57, 0x5d, 0x87, 0xb4, 0xa1, 0xd1, 0x1f, 0x0c, - 0x76, 0xf6, 0xb5, 0x67, 0x2a, 0x1a, 0xda, 0xde, 0x19, 0xbc, 0xd8, 0xdd, 0xdb, 0xd9, 0xee, 0x56, - 0x35, 0x34, 0xe8, 0xef, 0x0d, 0x76, 0x5e, 0xec, 0x6c, 0x77, 0x6b, 0xee, 0xbf, 0x1c, 0xd3, 0xd9, - 0x07, 0xa5, 0x95, 0x69, 0x9b, 0xfb, 0x42, 0x2e, 0xfe, 0xc5, 0x65, 0x03, 0x9a, 0xd6, 0x9e, 0xbb, - 0x79, 0xa4, 0x4d, 0x11, 0xe4, 0xe7, 0xb0, 0x12, 0x58, 0x7e, 0xaf, 0x14, 0x79, 0x0f, 0x67, 0x67, - 0xa4, 0x79, 0x57, 0x6e, 0xe5, 0x07, 0x6b, 0x9e, 0x4e, 0x50, 0x82, 0xdd, 0xfb, 0xd0, 0x29, 0x53, - 0x94, 0x1e, 0xfb, 0x41, 0xe9, 0xb1, 0x8e, 0xfb, 0x8d, 0x03, 0x2b, 0x33, 0xbf, 0x24, 0x2f, 0xee, - 0x36, 0xb3, 0xcb, 0x61, 0xe5, 0xdc, 0x72, 0x48, 0xee, 0x03, 0x29, 0x92, 0x78, 0xc5, 0x29, 0xbb, - 0x5b, 0x20, 0x34, 0xdb, 0x66, 0xb1, 0x7d, 0xd5, 0xde, 0xab, 0x7d, 0x49, 0x00, 0xca, 0xde, 0xd9, - 0x51, 0xaf, 0xd8, 0xd6, 0x9d, 0x72, 0x5b, 0x7f, 0x0e, 0x2d, 0xfb, 0xaf, 0x90, 0x43, 0xdd, 0x7b, - 0x2a, 0x68, 0xe7, 0x6f, 0x4f, 0x2f, 0xe9, 0x4f, 0xff, 0x79, 0xf2, 0xd2, 0xfe, 0xef, 0xc4, 0x0a, - 0xdd, 0xd2, 0x0c, 0xb4, 0xc8, 0xed, 0xfe, 0xc1, 0x81, 0x8e, 0xd6, 0xaa, 0x70, 0xf3, 0x0f, 0xa0, - 0x95, 0x4e, 0xa0, 0xbc, 0x0b, 0xac, 0x4e, 0xe5, 0x4f, 0x49, 0x69, 0x91, 0x90, 0x3c, 0x80, 0x55, - 0x39, 0x3e, 0xce, 0x3b, 0xc9, 0x33, 0x19, 0x47, 0x4f, 0x32, 0xc5, 0xf3, 0xfe, 0x3a, 0xf7, 0x1b, - 0xb9, 0x0f, 0x57, 0xf3, 0xa5, 0x6b, 0xca, 0x60, 0x36, 0xd1, 0xf3, 0x1f, 0x9e, 0x2c, 0xff, 0xac, - 0xb5, 0xf5, 0xc9, 0xa3, 0x5c, 0x91, 0xe3, 0x25, 0x3c, 0x3d, 0xfc, 0x4f, 0x00, 0x00, 0x00, 0xff, - 0xff, 0xa8, 0x44, 0x31, 0x9e, 0x5d, 0x1a, 0x00, 0x00, + // 2596 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x39, 0x4d, 0x73, 0x1b, 0xc7, + 0xb1, 0x5e, 0x00, 0xc4, 0x47, 0x03, 0x04, 0xa1, 0x11, 0x2d, 0x41, 0x14, 0x5d, 0x92, 0xd6, 0x76, + 0x59, 0xaf, 0xca, 0x8f, 0x7e, 0x4f, 0x4e, 0xe2, 0xc4, 0x1f, 0x65, 0x43, 0x00, 0x62, 0xd1, 0x14, + 0x41, 0xd6, 0x90, 0x90, 0x63, 0x57, 0xaa, 0xb6, 0x86, 0xbb, 0x23, 0x62, 0xc3, 0xc5, 0xee, 0x66, + 0x67, 0x40, 0x05, 0xb9, 0xe5, 0x92, 0x1f, 0x90, 0x4b, 0x72, 0xf4, 0x39, 0xb9, 0xa5, 0xca, 0x39, + 0xe5, 0x90, 0x63, 0x6e, 0x39, 0xe4, 0x92, 0xaa, 0xe4, 0x17, 0xe4, 0x57, 0xa4, 0xa6, 0x67, 0x16, + 0xbb, 0x0b, 0x02, 0x8c, 0x54, 0x39, 0xe5, 0xc4, 0xe9, 0xde, 0xee, 0x9e, 0x9e, 0xfe, 0x6e, 0x10, + 0x36, 0x63, 0xe6, 0x27, 0x7e, 0x78, 0xbe, 0x17, 0x27, 0x91, 0x8c, 0x48, 0x1d, 0xff, 0x9c, 0xcd, + 0x9e, 0xef, 0xdc, 0x14, 0xf3, 0xd0, 0x75, 0x04, 0x97, 0xd2, 0x0f, 0xcf, 0x85, 0xfe, 0xbc, 0x63, + 0xb3, 0x38, 0x0e, 0x7c, 0x97, 0x49, 0x3f, 0x0a, 0x9d, 0x29, 0x97, 0xcc, 0x63, 0x92, 0x39, 0x53, + 0x2e, 0x04, 0x3b, 0xe7, 0x9a, 0xc6, 0x66, 0x70, 0xf7, 0x87, 0x5c, 0xba, 0x13, 0x3f, 0x3c, 0x7f, + 0xcc, 0xdc, 0x0b, 0xee, 0x8d, 0xe3, 0x01, 0x93, 0x6c, 0xc0, 0x25, 0xf3, 0x03, 0x41, 0xee, 0x41, + 0x13, 0x99, 0xc2, 0xd9, 0xf4, 0x8c, 0x27, 0x5d, 0xeb, 0xbe, 0xf5, 0x70, 0x93, 0x82, 0x42, 0x8d, + 0x10, 0x43, 0x1e, 0x40, 0x4b, 0x46, 0x92, 0x05, 0x29, 0x45, 0x09, 0x29, 0x9a, 0x88, 0xd3, 0x24, + 0xf6, 0xdf, 0x2a, 0x50, 0x55, 0xb2, 0x67, 0x31, 0xd9, 0x86, 0x0d, 0x37, 0x88, 0xdc, 0x0b, 0x14, + 0x54, 0xa1, 0x1a, 0x20, 0x6d, 0x28, 0xf9, 0x1e, 0x72, 0x36, 0x68, 0xc9, 0xf7, 0xc8, 0xa7, 0x50, + 0x77, 0xa3, 0x50, 0x32, 0x57, 0x8a, 0x6e, 0xf9, 0x7e, 0xf9, 0x61, 0xf3, 0xd1, 0x9b, 0x7b, 0xe9, + 0x4b, 0xf7, 0x4e, 0xe6, 0xa1, 0xbb, 0x1f, 0x0a, 0xc9, 0x82, 0x00, 0x1f, 0xd6, 0xd7, 0x94, 0xcf, + 0x1e, 0xd1, 0x05, 0x13, 0xf9, 0x01, 0x34, 0xdd, 0x68, 0x3a, 0x9d, 0x85, 0xbe, 0xf4, 0xb9, 0xe8, + 0x56, 0x50, 0xc6, 0xed, 0xa2, 0x8c, 0xbe, 0x21, 0x98, 0xd3, 0x3c, 0x2d, 0x39, 0x82, 0xad, 0x54, + 0x8c, 0xb1, 0x41, 0x77, 0xe3, 0xbe, 0xf5, 0xb0, 0xf9, 0xe8, 0xed, 0x8c, 0xfd, 0x1a, 0x83, 0xd1, + 0x65, 0x6e, 0x32, 0x06, 0x92, 0x93, 0x9f, 0xca, 0xac, 0xbe, 0x8a, 0xcc, 0x15, 0x02, 0xc8, 0xfb, + 0x50, 0x8b, 0x93, 0xe8, 0xb9, 0x1f, 0xf0, 0x6e, 0x0d, 0x65, 0xdd, 0xc9, 0x64, 0xa5, 0x32, 0x8e, + 0x35, 0x01, 0x4d, 0x29, 0xc9, 0x21, 0xb4, 0xcd, 0x31, 0xd5, 0xa3, 0xfe, 0x2a, 0x7a, 0x2c, 0x31, + 0x93, 0xf7, 0xa0, 0x66, 0x22, 0xae, 0xdb, 0x40, 0x39, 0xaf, 0x17, 0x4d, 0x7c, 0xa2, 0x3f, 0xd2, + 0x94, 0x4a, 0x19, 0x37, 0x0d, 0xd1, 0x54, 0x01, 0x78, 0x25, 0xe3, 0x2e, 0x71, 0xdb, 0x7f, 0xac, + 0x40, 0xeb, 0x70, 0x16, 0x48, 0xbf, 0xe7, 0xba, 0xd1, 0x2c, 0x94, 0x84, 0x40, 0x25, 0x64, 0x53, + 0x8e, 0xf1, 0xd5, 0xa0, 0x78, 0x26, 0xbb, 0xd0, 0x90, 0xfe, 0x94, 0x0b, 0xc9, 0xa6, 0x31, 0x46, + 0x59, 0x99, 0x66, 0x08, 0xf5, 0xd5, 0xf7, 0x78, 0x28, 0x7d, 0x37, 0x0a, 0xbb, 0x65, 0x64, 0xcb, + 0x10, 0xe4, 0x33, 0x00, 0x37, 0x0a, 0xa2, 0xc4, 0x99, 0x30, 0x31, 0x31, 0x81, 0xf4, 0x20, 0x53, + 0x36, 0x7f, 0xf7, 0x5e, 0x3f, 0x0a, 0xa2, 0x59, 0xf2, 0x84, 0x89, 0x09, 0x6d, 0x20, 0x93, 0x3a, + 0x92, 0x2e, 0xd4, 0x10, 0xd8, 0xf7, 0x30, 0x90, 0xca, 0x34, 0x05, 0xc9, 0x3b, 0xb0, 0x75, 0xc1, + 0xe7, 0x2e, 0x4b, 0x3c, 0xc7, 0xa4, 0x35, 0x86, 0x45, 0x83, 0xb6, 0x0d, 0xfa, 0x58, 0x63, 0xc9, + 0x6d, 0xa8, 0x5d, 0xf0, 0xb9, 0x33, 0xf3, 0x3d, 0xf4, 0x75, 0x83, 0x56, 0x2f, 0xf8, 0x7c, 0xec, + 0x7b, 0xe4, 0x63, 0xa8, 0xfa, 0x53, 0x76, 0xce, 0x95, 0x1f, 0x95, 0x66, 0x6f, 0xad, 0xd1, 0x6c, + 0x1f, 0xdf, 0x23, 0xe7, 0xfb, 0x8a, 0x98, 0x1a, 0x9e, 0x1d, 0x1b, 0x20, 0x53, 0x59, 0xa5, 0xa6, + 0x1f, 0x7a, 0xfc, 0x67, 0x5d, 0xeb, 0x7e, 0xf9, 0x61, 0x99, 0x6a, 0x60, 0xe7, 0xef, 0x16, 0x6c, + 0x16, 0xb8, 0xf3, 0xca, 0x58, 0x05, 0x65, 0x52, 0xd3, 0x97, 0x72, 0xa6, 0xef, 0x42, 0x2d, 0x66, + 0xf3, 0x20, 0x62, 0x1e, 0x9a, 0xb6, 0x45, 0x53, 0x50, 0x5d, 0xf7, 0xc2, 0xf7, 0xa4, 0xb2, 0xa9, + 0x32, 0x8a, 0x06, 0xc8, 0x2d, 0xa8, 0x4e, 0xb8, 0x7f, 0x3e, 0x91, 0xc6, 0x56, 0x06, 0x22, 0x3b, + 0x50, 0x57, 0x81, 0x27, 0xfc, 0x9f, 0x73, 0xb4, 0x51, 0x99, 0x2e, 0x60, 0xf2, 0x26, 0x6c, 0x26, + 0x78, 0x72, 0x24, 0x4b, 0xce, 0xb9, 0x44, 0x1b, 0x95, 0x69, 0x4b, 0x23, 0x4f, 0x11, 0x97, 0x15, + 0x9e, 0x7a, 0xae, 0xf0, 0xd8, 0x7f, 0xb5, 0xe0, 0xe6, 0xd3, 0xc8, 0x65, 0x81, 0xb1, 0xf4, 0xb1, + 0x51, 0xee, 0xbb, 0x50, 0xb9, 0xe0, 0x73, 0x81, 0xa6, 0x28, 0xf8, 0x7b, 0x05, 0xf1, 0xde, 0x01, + 0x9f, 0x53, 0x24, 0x27, 0x1f, 0x42, 0x6b, 0xaa, 0xcc, 0xce, 0xb4, 0xd9, 0xd1, 0x12, 0xcd, 0x47, + 0xb7, 0x56, 0x3b, 0x85, 0x16, 0x68, 0xd5, 0x0b, 0x63, 0x26, 0xc4, 0x8b, 0x28, 0xf1, 0x4c, 0x14, + 0x2e, 0xe0, 0x9d, 0xff, 0x85, 0xf2, 0x01, 0x9f, 0xaf, 0x8c, 0x6d, 0x02, 0x15, 0x55, 0x8c, 0xf1, + 0xaa, 0x16, 0xc5, 0xb3, 0xfd, 0x4b, 0x0b, 0x3a, 0x4a, 0xc7, 0x7c, 0x95, 0x5c, 0x53, 0x79, 0xdf, + 0x81, 0x2d, 0x3f, 0x47, 0xe5, 0x2c, 0xca, 0x70, 0x3b, 0x8f, 0xde, 0xf7, 0xb0, 0x0f, 0xf0, 0x4b, + 0xdf, 0xe5, 0x8e, 0x9c, 0xc7, 0xdc, 0x68, 0x08, 0x1a, 0x75, 0x3a, 0x8f, 0xf9, 0x42, 0xb9, 0x4a, + 0xa6, 0x9c, 0xfd, 0x4f, 0x0b, 0x6e, 0xaf, 0x29, 0xd7, 0x2f, 0xd9, 0x09, 0xde, 0x84, 0x4d, 0x53, + 0x73, 0x1c, 0x0c, 0x5a, 0x73, 0x71, 0xcb, 0x20, 0x75, 0x44, 0xde, 0x81, 0x3a, 0x0f, 0x85, 0x93, + 0xbb, 0xbe, 0xc6, 0x43, 0x31, 0x52, 0xe6, 0x79, 0x00, 0xad, 0x80, 0x09, 0xe9, 0xcc, 0x62, 0x8f, + 0x49, 0xae, 0x33, 0xb0, 0x42, 0x9b, 0x0a, 0x37, 0xd6, 0x28, 0xf5, 0x32, 0x31, 0x17, 0x92, 0x4f, + 0x1d, 0xc9, 0xce, 0x55, 0x61, 0x2e, 0xab, 0x97, 0x69, 0xd4, 0x29, 0x3b, 0x17, 0xe4, 0x6d, 0x68, + 0x07, 0xca, 0xed, 0x4e, 0xe8, 0xbb, 0x17, 0x78, 0x89, 0x4e, 0xc2, 0x4d, 0xc4, 0x8e, 0x0c, 0xd2, + 0xfe, 0x45, 0x15, 0xee, 0xac, 0xed, 0x4d, 0xe4, 0xff, 0x60, 0x3b, 0xaf, 0x88, 0x83, 0xbc, 0xc1, + 0xdc, 0xbc, 0x9e, 0xe4, 0x14, 0x7a, 0xaa, 0xbf, 0xfc, 0x17, 0x9b, 0x42, 0xf9, 0x96, 0x79, 0x1e, + 0xf7, 0xb0, 0x2b, 0xd4, 0xa9, 0x06, 0x54, 0x2d, 0x38, 0x53, 0x4e, 0xe6, 0x1e, 0x16, 0xfd, 0x3a, + 0x4d, 0x41, 0x45, 0x3f, 0x9d, 0x29, 0x9d, 0x9a, 0x9a, 0x1e, 0x01, 0x45, 0x9f, 0xf0, 0x69, 0x74, + 0xc9, 0xbd, 0x6e, 0x4b, 0xd3, 0x1b, 0x90, 0xdc, 0x87, 0xd6, 0x84, 0x09, 0x07, 0xc5, 0x3a, 0x33, + 0xd1, 0xdd, 0xc4, 0xcf, 0x30, 0x61, 0xa2, 0xa7, 0x50, 0x63, 0xd5, 0x99, 0x6e, 0x5e, 0xf2, 0xc4, + 0x7f, 0x9e, 0x0e, 0x3f, 0x42, 0x32, 0x39, 0x13, 0xdd, 0x36, 0x56, 0x06, 0x92, 0xff, 0x74, 0x82, + 0x5f, 0x70, 0x8c, 0x49, 0x66, 0x42, 0xa6, 0x94, 0x5b, 0x48, 0xd9, 0x44, 0x9c, 0x21, 0xf9, 0x04, + 0xee, 0x9a, 0xde, 0xee, 0x24, 0xfc, 0xa7, 0x33, 0x2e, 0xa4, 0xf6, 0x22, 0xb2, 0xf0, 0x6e, 0x07, + 0x39, 0xba, 0x86, 0x84, 0x6a, 0x0a, 0x74, 0xa6, 0xe2, 0xe7, 0xeb, 0xd9, 0x75, 0x1a, 0xdc, 0x58, + 0xcb, 0xde, 0xc7, 0xcc, 0xf8, 0x14, 0x76, 0x97, 0xd9, 0x95, 0x39, 0x24, 0x37, 0xd7, 0x13, 0xe4, + 0xbf, 0x53, 0xe4, 0xa7, 0x48, 0xa1, 0xef, 0x5f, 0x2f, 0x40, 0x2b, 0x70, 0x73, 0xbd, 0x00, 0xad, + 0xc1, 0x03, 0x68, 0x79, 0xbe, 0x88, 0x03, 0x36, 0xd7, 0xf1, 0xb5, 0x8d, 0xae, 0x6f, 0x1a, 0x9c, + 0x8a, 0x31, 0xfb, 0xc5, 0xd5, 0x7c, 0x4f, 0x1b, 0xf3, 0xea, 0x7c, 0xbf, 0x12, 0xd4, 0xa5, 0x15, + 0x41, 0xbd, 0x1c, 0xb9, 0xe5, 0x2b, 0x91, 0x6b, 0x3f, 0x86, 0x9d, 0xe5, 0x8b, 0x8f, 0x67, 0x67, + 0x81, 0xef, 0xf6, 0x27, 0xec, 0x25, 0x6b, 0x8d, 0xfd, 0x6d, 0x19, 0x36, 0x0b, 0x83, 0xe1, 0xbf, + 0xe5, 0x6b, 0x61, 0x62, 0xde, 0x83, 0x66, 0x9c, 0xf8, 0x97, 0x4c, 0x72, 0xe7, 0x82, 0xcf, 0x4d, + 0x9f, 0x03, 0x83, 0x52, 0x75, 0xfb, 0xbe, 0xaa, 0x9d, 0xc2, 0x4d, 0xfc, 0x58, 0xe9, 0x85, 0x79, + 0xd9, 0xa2, 0x79, 0x94, 0x6a, 0x7b, 0x3f, 0x89, 0xfc, 0xd0, 0x64, 0x65, 0x9d, 0x1a, 0x48, 0x35, + 0x05, 0x1d, 0xab, 0xdc, 0xc3, 0xb6, 0x57, 0xa7, 0x0b, 0x38, 0x4b, 0x9a, 0x5a, 0x3e, 0x69, 0x8e, + 0xa0, 0x63, 0xbc, 0x2b, 0x1c, 0x19, 0x39, 0x4a, 0x8e, 0x99, 0x0d, 0xde, 0x5e, 0x37, 0xfe, 0x1a, + 0xf2, 0xd3, 0xe8, 0x8b, 0xc8, 0x0f, 0x69, 0x3b, 0x29, 0xc0, 0xe4, 0x23, 0xa8, 0xa7, 0x43, 0x97, + 0x19, 0xf2, 0xee, 0xad, 0x11, 0x64, 0xa6, 0x3d, 0x41, 0x17, 0x0c, 0x6a, 0xb6, 0xe2, 0xa1, 0x9b, + 0xcc, 0x63, 0xb9, 0x48, 0xfa, 0x0c, 0xa1, 0xbe, 0x8a, 0x98, 0xbb, 0x92, 0x65, 0xa9, 0x9f, 0x21, + 0x54, 0x6b, 0x32, 0xa4, 0x2a, 0x81, 0xb1, 0x1d, 0xb7, 0xd0, 0x72, 0xed, 0x0c, 0x7d, 0xc0, 0xe7, + 0xc2, 0xfe, 0x8b, 0x05, 0x77, 0xaf, 0x79, 0x91, 0xf1, 0x97, 0xb5, 0xf0, 0xd7, 0x1b, 0x00, 0x31, + 0xc6, 0x06, 0xba, 0x4b, 0xfb, 0xbf, 0xa1, 0x31, 0xca, 0x5b, 0x0b, 0xa7, 0x97, 0xf3, 0x4e, 0xbf, + 0xa6, 0xb0, 0xde, 0x86, 0x9a, 0x3b, 0x61, 0x52, 0xf5, 0xce, 0x0d, 0x3d, 0x10, 0x29, 0x70, 0xdf, + 0x53, 0x71, 0x9b, 0x0e, 0xee, 0x73, 0xf5, 0xb5, 0xaa, 0x1d, 0xbf, 0xc0, 0xed, 0xa3, 0x13, 0x75, + 0xfa, 0xd6, 0xf4, 0x65, 0x08, 0xd8, 0xbf, 0x2a, 0x41, 0x67, 0x39, 0x9c, 0xc9, 0x27, 0xb9, 0xa5, + 0xe8, 0xca, 0x5c, 0xb2, 0xa6, 0xf1, 0xe4, 0x56, 0xa2, 0xcf, 0xa1, 0x65, 0x5e, 0xad, 0xb4, 0x13, + 0xdd, 0xd2, 0xf2, 0xc0, 0xb8, 0x3e, 0x7f, 0x68, 0x33, 0x5e, 0x9c, 0x05, 0xf9, 0x08, 0x6a, 0xe9, + 0x7c, 0x53, 0xc6, 0x78, 0xb8, 0x46, 0x8d, 0x74, 0xd4, 0x49, 0x39, 0xfe, 0x83, 0xc5, 0xcc, 0xfe, + 0x00, 0xb6, 0xf0, 0xab, 0x52, 0xc8, 0xf4, 0x81, 0x97, 0xcb, 0xeb, 0x8f, 0x61, 0x3b, 0x65, 0x3c, + 0xd4, 0xab, 0xaf, 0xa0, 0x9c, 0xbd, 0x2c, 0xf7, 0x67, 0x70, 0x4b, 0x71, 0xf7, 0x5c, 0xe9, 0x5f, + 0xfa, 0x72, 0xde, 0xe7, 0xa1, 0xe4, 0xc9, 0x35, 0xfc, 0x1d, 0x28, 0xfb, 0x9e, 0x36, 0x6f, 0x8b, + 0xaa, 0xa3, 0x3d, 0xd0, 0xb5, 0xa9, 0x28, 0xa1, 0xe7, 0xba, 0x1c, 0x93, 0xe0, 0x65, 0xa5, 0x0c, + 0x75, 0x90, 0x17, 0xa5, 0x0c, 0x7c, 0x31, 0xf5, 0x85, 0x78, 0x05, 0x31, 0xdf, 0x58, 0xd0, 0x52, + 0x72, 0x1e, 0x47, 0xd1, 0xc5, 0x94, 0x25, 0x17, 0xeb, 0x19, 0x67, 0x49, 0x60, 0xcc, 0xa0, 0x8e, + 0x8b, 0xf9, 0xae, 0x9c, 0x1b, 0x3e, 0xef, 0x42, 0x03, 0xab, 0xb6, 0xa3, 0x68, 0x75, 0x56, 0xd4, + 0x11, 0x31, 0x4e, 0x82, 0x7c, 0xfb, 0xde, 0x28, 0xb6, 0xef, 0x37, 0x00, 0x3c, 0x1e, 0x70, 0x35, + 0x06, 0x31, 0x89, 0x59, 0x51, 0xa1, 0x0d, 0x83, 0xe9, 0x49, 0xfb, 0x0b, 0x1d, 0xfc, 0xfd, 0x80, + 0xb3, 0xe4, 0x89, 0x2f, 0x64, 0x94, 0xcc, 0xf3, 0x39, 0x66, 0x15, 0x72, 0xec, 0x0d, 0x00, 0x57, + 0x11, 0x6a, 0x59, 0x25, 0x2d, 0xcb, 0x60, 0x7a, 0xd2, 0xfe, 0xb3, 0x05, 0x44, 0x09, 0x33, 0x9b, + 0xf0, 0xb1, 0xef, 0xca, 0x59, 0xc2, 0x57, 0x4e, 0xd2, 0xb9, 0x55, 0xa5, 0xb4, 0x66, 0x55, 0x29, + 0xe3, 0x6f, 0x1b, 0x57, 0x56, 0x95, 0x0a, 0xa2, 0xd3, 0x55, 0xe5, 0x2e, 0x34, 0xb0, 0x9f, 0xe1, + 0xae, 0xb2, 0x81, 0x9f, 0x70, 0x57, 0x39, 0x59, 0xb9, 0xab, 0x54, 0x91, 0x60, 0xcd, 0xae, 0x52, + 0xcb, 0xef, 0x2a, 0x13, 0xb8, 0x79, 0xf5, 0x25, 0x62, 0xfd, 0x3a, 0xf6, 0x7d, 0xa8, 0xc7, 0x86, + 0xc8, 0x24, 0xfb, 0x6e, 0x31, 0xcf, 0x8a, 0x92, 0xe8, 0x82, 0xda, 0xfe, 0x5d, 0x09, 0x6e, 0x28, + 0x82, 0x2f, 0x59, 0x10, 0x70, 0x79, 0x7d, 0x03, 0xef, 0x42, 0x8d, 0x79, 0x5e, 0xc2, 0x85, 0x48, + 0xad, 0x66, 0x40, 0x65, 0x9f, 0x17, 0x28, 0x00, 0xcd, 0x56, 0xa7, 0x06, 0x52, 0xb6, 0x57, 0xbe, + 0x43, 0xab, 0xd5, 0x29, 0x9e, 0x15, 0x0e, 0xd7, 0x0a, 0x5d, 0x3f, 0xf1, 0xac, 0x24, 0x2b, 0xdf, + 0xab, 0xa1, 0x40, 0x6f, 0xc5, 0x29, 0xa8, 0xa8, 0x63, 0x26, 0x27, 0x66, 0xf6, 0xc4, 0xb3, 0xea, + 0x25, 0x8b, 0x12, 0x8e, 0x3b, 0x5e, 0x2b, 0x5f, 0xd3, 0x53, 0x7f, 0x37, 0x72, 0xfe, 0x56, 0xef, + 0x51, 0x8b, 0x38, 0xf6, 0xa5, 0x06, 0xd5, 0x00, 0x7a, 0xd5, 0xf7, 0x3c, 0x1e, 0x9a, 0x86, 0x64, + 0xa0, 0xf5, 0xc3, 0xa8, 0x7d, 0xa8, 0x23, 0xac, 0x60, 0x2c, 0x41, 0x3e, 0x80, 0xba, 0xa9, 0x79, + 0x69, 0xb5, 0xbe, 0x5b, 0xb4, 0x7e, 0x81, 0x9e, 0x2e, 0x88, 0xed, 0x3f, 0x58, 0x3a, 0xfc, 0x4f, + 0xd8, 0x25, 0xf7, 0x7a, 0xc6, 0x96, 0x39, 0x2b, 0x5b, 0x45, 0x2b, 0xaf, 0x5a, 0xba, 0x77, 0xa1, + 0xf1, 0x9c, 0x5d, 0x46, 0xb3, 0xc4, 0x97, 0xdc, 0x18, 0x3f, 0x43, 0xa8, 0x4e, 0xe6, 0x4e, 0x98, + 0x8f, 0xbb, 0x5e, 0x05, 0x5d, 0x59, 0x43, 0x78, 0xdf, 0xbb, 0x26, 0x65, 0x1f, 0x40, 0x4b, 0x4f, + 0x5f, 0x4e, 0x3e, 0x32, 0x9b, 0x1a, 0x87, 0xe3, 0xa1, 0xfd, 0x6b, 0x0b, 0x5e, 0x5f, 0x39, 0x0f, + 0xac, 0x89, 0x9c, 0xe5, 0xee, 0xa8, 0x5f, 0x50, 0xe8, 0x8e, 0x43, 0xb8, 0x37, 0xd1, 0x05, 0xc0, + 0x61, 0x89, 0x3b, 0xf1, 0x2f, 0xb9, 0x23, 0x66, 0x71, 0x1c, 0x25, 0xd2, 0xe1, 0x21, 0x3b, 0x0b, + 0xcc, 0x2c, 0x58, 0xa7, 0xbb, 0x86, 0xac, 0xa7, 0xa9, 0x4e, 0x34, 0xd1, 0x50, 0xd3, 0xd8, 0xbf, + 0xb7, 0x74, 0xeb, 0x38, 0x55, 0xc3, 0xbc, 0x5a, 0x0f, 0x78, 0xf2, 0x92, 0xeb, 0xe7, 0x27, 0x50, + 0x35, 0xfb, 0x80, 0xba, 0xa7, 0xbd, 0x3c, 0x43, 0xe5, 0x04, 0xee, 0x9d, 0x66, 0x9b, 0x02, 0x35, + 0x4c, 0xf6, 0x87, 0xd0, 0xcc, 0xa1, 0x49, 0x13, 0x6a, 0xe3, 0xd1, 0xc1, 0xe8, 0xe8, 0xcb, 0x51, + 0xe7, 0x35, 0x05, 0x9c, 0xd2, 0xf1, 0xc9, 0xe9, 0x70, 0xd0, 0xb1, 0xc8, 0x0d, 0xd8, 0x1c, 0x8f, + 0x10, 0xfc, 0xf2, 0x88, 0x9e, 0x3e, 0xf9, 0xaa, 0x53, 0xb2, 0xbf, 0x29, 0xeb, 0x59, 0xfa, 0x59, + 0x6e, 0x57, 0x31, 0x83, 0xcd, 0x1a, 0xe5, 0x09, 0x54, 0x9e, 0x27, 0xd1, 0x34, 0x0d, 0x05, 0x75, + 0x56, 0x0f, 0x92, 0x91, 0xa9, 0xd9, 0x25, 0x19, 0xa9, 0xd0, 0x70, 0x27, 0x2a, 0xf2, 0xc2, 0xf3, + 0x74, 0x8e, 0xc9, 0x10, 0xca, 0x25, 0x66, 0xfa, 0xd3, 0xe5, 0xd4, 0xac, 0x88, 0x0b, 0x5c, 0x0f, + 0x7f, 0xa6, 0x48, 0xb8, 0x88, 0xa3, 0x50, 0xa4, 0x69, 0xb9, 0x80, 0x55, 0x2d, 0x4e, 0x78, 0x1c, + 0xf8, 0x9a, 0x59, 0x87, 0x48, 0xc3, 0x60, 0x7a, 0x92, 0xf0, 0xd5, 0x3b, 0x59, 0x1d, 0x2d, 0xfb, + 0x9d, 0xa2, 0x65, 0x57, 0xbc, 0x7a, 0xef, 0xd9, 0x95, 0xad, 0x6d, 0xe5, 0x26, 0xa7, 0x7d, 0xd8, + 0x58, 0x34, 0xf0, 0x1f, 0x01, 0xb9, 0xca, 0x79, 0xc5, 0x17, 0xc7, 0xc3, 0xd1, 0x60, 0x7f, 0xf4, + 0x79, 0xc7, 0x22, 0x2d, 0xa8, 0xf7, 0xfa, 0xfd, 0xe1, 0xb1, 0xf2, 0x4c, 0x49, 0x41, 0x83, 0x61, + 0xff, 0xe9, 0xfe, 0x68, 0x38, 0xe8, 0x94, 0x15, 0xd4, 0xef, 0x8d, 0xfa, 0xc3, 0xa7, 0xc3, 0x41, + 0xa7, 0x62, 0xff, 0xc3, 0xd2, 0x9d, 0xbd, 0x5f, 0x58, 0x99, 0x06, 0xdc, 0xf5, 0xc5, 0xfa, 0x5f, + 0x5c, 0x76, 0xa1, 0x61, 0xec, 0xb9, 0x9f, 0x46, 0x5a, 0x86, 0x20, 0x3f, 0x86, 0x2d, 0xcf, 0xf0, + 0x3b, 0x85, 0xc8, 0x7b, 0x7f, 0x79, 0x46, 0x5a, 0x75, 0xe5, 0x5e, 0x7a, 0x30, 0xe6, 0x69, 0x7b, + 0x05, 0xd8, 0x7e, 0x17, 0xda, 0x45, 0x8a, 0xc2, 0x63, 0x5f, 0x2b, 0x3c, 0xd6, 0xb2, 0xbf, 0xb5, + 0x60, 0x6b, 0xe9, 0x97, 0xe4, 0xf5, 0xdd, 0x66, 0x79, 0x39, 0x2c, 0x5d, 0x59, 0x0e, 0xc9, 0xbb, + 0x40, 0xf2, 0x24, 0x4e, 0x7e, 0xca, 0xee, 0xe4, 0x08, 0xf5, 0xb6, 0x99, 0x6f, 0x5f, 0x95, 0x57, + 0x6a, 0x5f, 0x02, 0x80, 0xb2, 0x17, 0x66, 0xd4, 0xcb, 0xb7, 0x75, 0xab, 0xd8, 0xd6, 0x0f, 0xa0, + 0x69, 0xfe, 0x15, 0x72, 0xaa, 0x7a, 0x4f, 0x09, 0xed, 0xfc, 0x3f, 0xd9, 0x25, 0xbd, 0xec, 0x9f, + 0x27, 0x87, 0xe6, 0x7f, 0x27, 0x46, 0xe8, 0x9e, 0x62, 0xa0, 0x79, 0x6e, 0xfb, 0xb7, 0x16, 0xb4, + 0x95, 0x56, 0xb9, 0x9b, 0xbf, 0x07, 0xcd, 0x64, 0x01, 0xa5, 0x5d, 0x60, 0x3b, 0x93, 0x9f, 0x91, + 0xd2, 0x3c, 0x21, 0x79, 0x04, 0xdb, 0x62, 0x76, 0x96, 0x76, 0x92, 0x2f, 0x44, 0x14, 0x3e, 0x9e, + 0x4b, 0x9e, 0xf6, 0xd7, 0x95, 0xdf, 0xc8, 0xbb, 0x70, 0x23, 0x5d, 0xba, 0x32, 0x06, 0xbd, 0x89, + 0x5e, 0xfd, 0x60, 0xff, 0xc6, 0x82, 0xa6, 0x52, 0xf6, 0x40, 0xff, 0xcc, 0x8c, 0xd3, 0xde, 0xc2, + 0xa3, 0xea, 0xb8, 0xb2, 0xad, 0xdc, 0x82, 0xaa, 0xf9, 0xf9, 0xc6, 0x34, 0x74, 0xf3, 0xeb, 0x4d, + 0x2e, 0x26, 0x2a, 0x85, 0x98, 0xd8, 0x85, 0x86, 0x69, 0x53, 0x5c, 0x74, 0x37, 0x70, 0x06, 0xcd, + 0x10, 0x59, 0x7a, 0x54, 0xf3, 0x53, 0xce, 0x9f, 0xcc, 0xec, 0x61, 0x54, 0x53, 0xe3, 0x6e, 0x14, + 0x92, 0x0f, 0xa1, 0xca, 0xf0, 0x84, 0x3a, 0xb6, 0x1f, 0xd9, 0xc5, 0x50, 0x28, 0x10, 0xef, 0xe9, + 0x3f, 0xd4, 0x70, 0x90, 0xb7, 0x60, 0x33, 0x0a, 0x3c, 0x43, 0x32, 0x5e, 0x94, 0xf7, 0x22, 0x92, + 0xbc, 0x87, 0x8f, 0x50, 0x90, 0xd9, 0x6a, 0x5e, 0x5f, 0x79, 0x05, 0x4d, 0xa9, 0x54, 0xbb, 0xab, + 0x1a, 0xed, 0x6e, 0xc0, 0xe6, 0xc1, 0xf0, 0xab, 0x7e, 0x8f, 0x0e, 0x9c, 0xde, 0x60, 0x80, 0x99, + 0x44, 0xa0, 0xdd, 0xeb, 0xf7, 0x8f, 0xc6, 0xa3, 0xd3, 0x13, 0x83, 0xb3, 0xc8, 0x4d, 0xd8, 0x4a, + 0xc9, 0x06, 0xc3, 0xa7, 0x43, 0x5d, 0x5f, 0xb6, 0xa1, 0xb3, 0x20, 0xa4, 0xc3, 0xc3, 0xa3, 0x67, + 0x58, 0x67, 0x00, 0xaa, 0x4f, 0x8f, 0xfa, 0x07, 0xaa, 0xca, 0xa8, 0xa4, 0x1c, 0x8f, 0x0c, 0xb4, + 0x41, 0xb6, 0xa0, 0x39, 0xde, 0x1f, 0x38, 0xe3, 0xe3, 0x41, 0x4f, 0x09, 0xa8, 0x92, 0x0e, 0xb4, + 0x46, 0xbd, 0xc3, 0xa1, 0xd3, 0x7f, 0xd2, 0x1b, 0x7d, 0x3e, 0x1c, 0x74, 0x6a, 0xf6, 0xd7, 0xba, + 0xdb, 0xf5, 0x82, 0xc0, 0x28, 0x2d, 0xc8, 0xff, 0x43, 0xdd, 0xe8, 0x9d, 0xc6, 0xe1, 0x9a, 0xe7, + 0x2d, 0xc8, 0x32, 0xf7, 0x94, 0x72, 0xee, 0x79, 0xbc, 0xf9, 0x75, 0x73, 0xef, 0xbd, 0x8f, 0x52, + 0xd6, 0xb3, 0x2a, 0x9e, 0xde, 0xff, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd5, 0xde, 0xb0, 0x84, + 0x97, 0x1c, 0x00, 0x00, } diff --git a/protocol/protobuf/pairing.proto b/protocol/protobuf/pairing.proto index 01a29bc50..29fce7d86 100644 --- a/protocol/protobuf/pairing.proto +++ b/protocol/protobuf/pairing.proto @@ -298,3 +298,34 @@ message SyncRawMessage { bytes subAccountsJsonBytes = 2; bytes settingsJsonBytes = 3; } + +message SyncKeycard { + string uid = 1; + string name = 2; + bool locked = 3; + string key_uid = 4; + repeated bytes addresses = 5; + uint64 clock = 6; +} + +message SyncKeycardAction { + Action action = 1; + string oldKeycardUid = 2; + SyncKeycard keycard = 3; + + enum Action { + KEYCARD_ADDED = 0; + ACCOUNTS_ADDED = 1; + KEYCARD_DELETED = 2; + ACCOUNTS_REMOVED = 3; + LOCKED = 4; + UNLOCKED = 5; + UID_UPDATED = 6; + NAME_CHANGED = 7; + } +} + +message SyncAllKeycards { + repeated SyncKeycard keycards = 1; + uint64 clock = 2; +} \ No newline at end of file diff --git a/protocol/v1/status_message.go b/protocol/v1/status_message.go index 225c11545..d4be3646e 100644 --- a/protocol/v1/status_message.go +++ b/protocol/v1/status_message.go @@ -318,6 +318,10 @@ func (m *StatusMessage) HandleApplication() error { return m.unmarshalProtobufData((new(protobuf.SyncContactRequestDecision))) case protobuf.ApplicationMetadataMessage_SYNC_SAVED_ADDRESS: return m.unmarshalProtobufData(new(protobuf.SyncSavedAddress)) + case protobuf.ApplicationMetadataMessage_SYNC_ALL_KEYCARDS: + return m.unmarshalProtobufData(new(protobuf.SyncAllKeycards)) + case protobuf.ApplicationMetadataMessage_SYNC_KEYCARD_ACTION: + return m.unmarshalProtobufData(new(protobuf.SyncKeycardAction)) } return nil } diff --git a/services/accounts/accounts.go b/services/accounts/accounts.go index 2a6f70970..f689622b9 100644 --- a/services/accounts/accounts.go +++ b/services/accounts/accounts.go @@ -454,7 +454,7 @@ func (api *API) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx context.Cont kp.AccountsAddresses = append(kp.AccountsAddresses, types.Address(common.HexToAddress(addr))) } - added, err := api.db.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(kp) + added, err := (*api.messenger).AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx, &kp) if err != nil { return err } @@ -472,14 +472,8 @@ func (api *API) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx context.Cont } func (api *API) RemoveMigratedAccountsForKeycard(ctx context.Context, kcUID string, accountAddresses []string) error { - var addresses []types.Address - for _, addr := range accountAddresses { - addresses = append(addresses, types.Address(common.HexToAddress(addr))) - } - clock := uint64(time.Now().Unix()) - _, err := api.db.RemoveMigratedAccountsForKeycard(kcUID, addresses, clock) - return err + return (*api.messenger).RemoveMigratedAccountsForKeycard(ctx, kcUID, accountAddresses, clock) } func (api *API) GetAllKnownKeycards(ctx context.Context) ([]*keypairs.KeyPair, error) { @@ -496,26 +490,22 @@ func (api *API) GetMigratedKeyPairByKeyUID(ctx context.Context, keyUID string) ( func (api *API) SetKeycardName(ctx context.Context, kcUID string, kpName string) error { clock := uint64(time.Now().Unix()) - _, err := api.db.SetKeycardName(kcUID, kpName, clock) - return err + return (*api.messenger).SetKeycardName(ctx, kcUID, kpName, clock) } func (api *API) KeycardLocked(ctx context.Context, kcUID string) error { clock := uint64(time.Now().Unix()) - _, err := api.db.KeycardLocked(kcUID, clock) - return err + return (*api.messenger).KeycardLocked(ctx, kcUID, clock) } func (api *API) KeycardUnlocked(ctx context.Context, kcUID string) error { clock := uint64(time.Now().Unix()) - _, err := api.db.KeycardUnlocked(kcUID, clock) - return err + return (*api.messenger).KeycardUnlocked(ctx, kcUID, clock) } func (api *API) DeleteKeycard(ctx context.Context, kcUID string) error { clock := uint64(time.Now().Unix()) - _, err := api.db.DeleteKeycard(kcUID, clock) - return err + return (*api.messenger).DeleteKeycard(ctx, kcUID, clock) } func (api *API) DeleteKeypair(ctx context.Context, keyUID string) error { @@ -524,6 +514,5 @@ func (api *API) DeleteKeypair(ctx context.Context, keyUID string) error { func (api *API) UpdateKeycardUID(ctx context.Context, oldKcUID string, newKcUID string) error { clock := uint64(time.Now().Unix()) - _, err := api.db.UpdateKeycardUID(oldKcUID, newKcUID, clock) - return err + return (*api.messenger).UpdateKeycardUID(ctx, oldKcUID, newKcUID, clock) }