chore: synchronization improvements applied to keypairs/accounts

This is the first step of improvements over keypairs/keycards/accounts.
- `SyncKeypairFull` protobuf removed
- `SyncKeypair` protobuf is used for syncing all but the watch only accounts
- `SyncAccount` is used only for syncing watch only accounts
- related keycards are synced together with a keypair
- on any keypair change (either it's just a keypair name or any change made over an
account which belongs to that keypair) entire keypair is synced including related keycards
- on any watch only account related change, that account is synced with all its details
This commit is contained in:
Sale Djenic 2023-06-28 21:45:36 +02:00 committed by saledjenic
parent b5d5fa828c
commit 61527f8c78
28 changed files with 1279 additions and 1054 deletions

View File

@ -1 +1 @@
0.161.0
0.161.1

View File

@ -67,6 +67,7 @@ type Manager interface {
GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*SelectedExtKey, error)
Sign(rpcParams SignParams, verifiedAccount *SelectedExtKey) (result types.HexBytes, err error)
CanRecover(rpcParams RecoverParams, revealedAddress types.Address) (bool, error)
DeleteAccount(address types.Address) error
}
// DefaultManager represents default account manager implementation

View File

@ -18,6 +18,7 @@ import (
)
const (
statusChatPath = "m/43'/60'/1581'/0'/0"
statusWalletRootPath = "m/44'/60'/0'/0/"
zeroAddress = "0x0000000000000000000000000000000000000000"
SyncedFromBackup = "backup" // means a account is coming from backed up data
@ -25,6 +26,7 @@ const (
)
var (
errDbPassedParameterIsNil = errors.New("accounts: passed parameter is nil")
errDbTransactionIsNil = errors.New("accounts: database transaction is nil")
ErrDbKeypairNotFound = errors.New("accounts: keypair is not found")
ErrDbAccountNotFound = errors.New("accounts: account is not found")
@ -40,7 +42,9 @@ type Keypair struct {
LastUsedDerivationIndex uint64 `json:"last-used-derivation-index,omitempty"`
SyncedFrom string `json:"synced-from,omitempty"` // keeps an info which device this keypair is added from can be one of two values defined in constants or device name (custom)
Clock uint64 `json:"clock,omitempty"`
Accounts []*Account `json:"accounts"`
Accounts []*Account `json:"accounts,omitempty"`
Keycards []*Keycard `json:"keycards,omitempty"`
Removed bool `json:"removed,omitempty"`
}
type Account struct {
@ -156,6 +160,8 @@ func (a *Keypair) MarshalJSON() ([]byte, error) {
SyncedFrom string `json:"synced-from"`
Clock uint64 `json:"clock"`
Accounts []*Account `json:"accounts"`
Keycards []*Keycard `json:"keycards"`
Removed bool `json:"removed"`
}{
KeyUID: a.KeyUID,
Name: a.Name,
@ -165,6 +171,8 @@ func (a *Keypair) MarshalJSON() ([]byte, error) {
SyncedFrom: a.SyncedFrom,
Clock: a.Clock,
Accounts: a.Accounts,
Keycards: a.Keycards,
Removed: a.Removed,
}
return json.Marshal(item)
@ -180,6 +188,8 @@ func (a *Keypair) CopyKeypair() *Keypair {
LastUsedDerivationIndex: a.LastUsedDerivationIndex,
SyncedFrom: a.SyncedFrom,
Accounts: make([]*Account, len(a.Accounts)),
Keycards: make([]*Keycard, len(a.Keycards)),
Removed: a.Removed,
}
for i, acc := range a.Accounts {
@ -203,6 +213,17 @@ func (a *Keypair) CopyKeypair() *Keypair {
}
}
for i, kc := range a.Keycards {
kp.Keycards[i] = &Keycard{
KeycardUID: kc.KeycardUID,
KeycardName: kc.KeycardName,
KeycardLocked: kc.KeycardLocked,
AccountsAddresses: kc.AccountsAddresses,
KeyUID: kc.KeyUID,
LastUpdateClock: kc.LastUpdateClock,
}
}
return kp
}
@ -248,7 +269,7 @@ func (db *Database) Close() error {
return db.db.Close()
}
func getAccountTypeForKeypairType(kpType KeypairType) AccountType {
func GetAccountTypeForKeypairType(kpType KeypairType) AccountType {
switch kpType {
case KeypairTypeProfile:
return AccountTypeGenerated
@ -368,7 +389,7 @@ func (db *Database) processKeypairs(rows *sql.Rows) ([]*Keypair, error) {
acc.PublicKey = make(types.HexBytes, lth)
copy(acc.PublicKey, pubkey)
}
acc.Type = getAccountTypeForKeypairType(kp.Type)
acc.Type = GetAccountTypeForKeypairType(kp.Type)
if _, ok := keypairMap[kp.KeyUID]; !ok {
keypairMap[kp.KeyUID] = kp
@ -396,6 +417,21 @@ func (db *Database) getKeypairs(tx *sql.Tx, keyUID string) ([]*Keypair, error) {
err error
where string
)
if tx == nil {
tx, err = db.db.Begin()
defer func() {
if err == nil {
err = tx.Commit()
return
}
_ = tx.Rollback()
}()
if err != nil {
return nil, err
}
}
if keyUID != "" {
where = "WHERE k.key_uid = ?"
}
@ -427,35 +463,38 @@ func (db *Database) getKeypairs(tx *sql.Tx, keyUID string) ([]*Keypair, error) {
ORDER BY
ka.position`, where)
if tx == nil {
if where != "" {
rows, err = db.db.Query(query, keyUID)
} else {
rows, err = db.db.Query(query)
}
if err != nil {
return nil, err
}
} else {
stmt, err := tx.Prepare(query)
if err != nil {
return nil, err
}
defer stmt.Close()
stmt, err := tx.Prepare(query)
if err != nil {
return nil, err
}
defer stmt.Close()
if where != "" {
rows, err = stmt.Query(keyUID)
} else {
rows, err = stmt.Query()
}
if err != nil {
return nil, err
}
if where != "" {
rows, err = stmt.Query(keyUID)
} else {
rows, err = stmt.Query()
}
if err != nil {
return nil, err
}
defer rows.Close()
return db.processKeypairs(rows)
keypairs, err := db.processKeypairs(rows)
if err != nil {
return nil, err
}
for _, kp := range keypairs {
keycards, err := db.getAllRows(tx, true)
if err != nil {
return nil, err
}
kp.Keycards = keycards
}
return keypairs, nil
}
func (db *Database) getKeypairByKeyUID(tx *sql.Tx, keyUID string) (*Keypair, error) {
@ -624,7 +663,7 @@ func (db *Database) GetWatchOnlyAccounts() (res []*Account, err error) {
return
}
func (db *Database) IsAnyAccountPartalyOrFullyOperableForKeyUID(keyUID string) (bool, error) {
func (db *Database) IsAnyAccountPartiallyOrFullyOperableForKeyUID(keyUID string) (bool, error) {
kp, err := db.getKeypairByKeyUID(nil, keyUID)
if err != nil {
return false, err
@ -642,7 +681,7 @@ func (db *Database) DeleteKeypair(keyUID string) error {
return db.deleteKeypair(nil, keyUID)
}
func (db *Database) DeleteAccount(address types.Address) error {
func (db *Database) DeleteAccount(address types.Address, clock uint64) error {
tx, err := db.db.Begin()
defer func() {
if err == nil {
@ -683,38 +722,72 @@ func (db *Database) DeleteAccount(address types.Address) error {
defer delete.Close()
_, err = delete.Exec(address)
if err != nil {
return err
}
// Update keypair clock if any but the watch only account was deleted.
if kp != nil {
err = db.updateKeypairClock(tx, acc.KeyUID, clock)
return err
}
return nil
}
func updateKeypairLastUsedIndex(tx *sql.Tx, keyUID string, index uint64, clock uint64, updateKeypairClock bool) error {
if tx == nil {
return errDbTransactionIsNil
}
var (
err error
setClock string
)
if updateKeypairClock {
setClock = ", clock = ?"
}
query := fmt.Sprintf( // nolint: gosec
`
UPDATE
keypairs
SET
last_used_derivation_index = ?
%s
WHERE
key_uid = ?`, setClock)
if setClock != "" {
_, err = tx.Exec(query, index, clock, keyUID)
} else {
_, err = tx.Exec(query, index, keyUID)
}
return err
}
func updateKeypairLastUsedIndex(tx *sql.Tx, keyUID string, index uint64, clock uint64) error {
func (db *Database) updateKeypairClock(tx *sql.Tx, keyUID string, clock uint64) error {
if tx == nil {
return errDbTransactionIsNil
}
_, err := tx.Exec(`
UPDATE
keypairs
SET
last_used_derivation_index = ?,
clock = ?
WHERE
key_uid = ?`,
index, clock, keyUID)
clock, keyUID)
return err
}
func (db *Database) saveOrUpdateAccounts(tx *sql.Tx, accounts []*Account) (err error) {
func (db *Database) saveOrUpdateAccounts(tx *sql.Tx, accounts []*Account, updateKeypairClock bool) (err error) {
if tx == nil {
return errDbTransactionIsNil
}
var maxPosition uint64
err = tx.QueryRow("SELECT MAX(position) FROM keypairs_accounts").Scan(&maxPosition)
if err != nil {
maxPosition = 0
}
for _, acc := range accounts {
var relatedKeypair *Keypair
// only watch only accounts have an empty `KeyUID` field
@ -736,19 +809,12 @@ func (db *Database) saveOrUpdateAccounts(tx *sql.Tx, accounts []*Account) (err e
return err
}
if !exists {
maxPosition++
_, err = tx.Exec(`
INSERT OR IGNORE INTO
keypairs_accounts (address, key_uid, pubkey, path, wallet, chat, position, created_at, updated_at)
VALUES
(?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'));
`, acc.Address, keyUID, acc.PublicKey, acc.Path, acc.Wallet, acc.Chat, maxPosition)
if err != nil {
return err
}
}
_, err = tx.Exec(`
INSERT OR IGNORE INTO
keypairs_accounts (address, key_uid, pubkey, path, wallet, chat, created_at, updated_at)
VALUES
(?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'));
UPDATE
keypairs_accounts
SET
@ -757,15 +823,27 @@ func (db *Database) saveOrUpdateAccounts(tx *sql.Tx, accounts []*Account) (err e
emoji = ?,
hidden = ?,
operable = ?,
clock = ?
clock = ?,
position = ?,
updated_at = datetime('now')
WHERE
address = ?;
`, acc.Name, acc.ColorID, acc.Emoji, acc.Hidden, acc.Operable, acc.Clock, acc.Address)
`,
acc.Address, keyUID, acc.PublicKey, acc.Path, acc.Wallet, acc.Chat,
acc.Name, acc.ColorID, acc.Emoji, acc.Hidden, acc.Operable, acc.Clock, acc.Position, acc.Address)
if err != nil {
return err
}
// Update keypair clock if any but the watch only account has changed.
if relatedKeypair != nil && updateKeypairClock {
err = db.updateKeypairClock(tx, acc.KeyUID, acc.Clock)
if err != nil {
return err
}
}
if strings.HasPrefix(acc.Path, statusWalletRootPath) {
accIndex, err := strconv.ParseUint(acc.Path[len(statusWalletRootPath):], 0, 64)
if err != nil {
@ -781,16 +859,19 @@ func (db *Database) saveOrUpdateAccounts(tx *sql.Tx, accounts []*Account) (err e
return false
}
expectedNewKeypairIndex := relatedKeypair.LastUsedDerivationIndex
for {
expectedNewKeypairIndex++
if !accountsContainPath(relatedKeypair.Accounts, statusWalletRootPath+strconv.FormatUint(expectedNewKeypairIndex, 10)) {
break
expectedNewKeypairIndex := uint64(0)
if relatedKeypair != nil {
expectedNewKeypairIndex = relatedKeypair.LastUsedDerivationIndex
for {
expectedNewKeypairIndex++
if !accountsContainPath(relatedKeypair.Accounts, statusWalletRootPath+strconv.FormatUint(expectedNewKeypairIndex, 10)) {
break
}
}
}
if accIndex == expectedNewKeypairIndex {
err = updateKeypairLastUsedIndex(tx, acc.KeyUID, accIndex, acc.Clock)
err = updateKeypairLastUsedIndex(tx, acc.KeyUID, accIndex, acc.Clock, updateKeypairClock)
if err != nil {
return err
}
@ -801,7 +882,7 @@ func (db *Database) saveOrUpdateAccounts(tx *sql.Tx, accounts []*Account) (err e
return nil
}
func (db *Database) SaveOrUpdateAccounts(accounts []*Account) error {
func (db *Database) SaveOrUpdateAccounts(accounts []*Account, updateKeypairClock bool) error {
if len(accounts) == 0 {
return errors.New("no provided accounts to save/update")
}
@ -817,11 +898,15 @@ func (db *Database) SaveOrUpdateAccounts(accounts []*Account) error {
}
_ = tx.Rollback()
}()
err = db.saveOrUpdateAccounts(tx, accounts)
err = db.saveOrUpdateAccounts(tx, accounts, updateKeypairClock)
return err
}
func (db *Database) SaveOrUpdateKeypair(keypair *Keypair) error {
if keypair == nil {
return errDbPassedParameterIsNil
}
tx, err := db.db.Begin()
if err != nil {
return err
@ -870,10 +955,10 @@ func (db *Database) SaveOrUpdateKeypair(keypair *Keypair) error {
if err != nil {
return err
}
return db.saveOrUpdateAccounts(tx, keypair.Accounts)
return db.saveOrUpdateAccounts(tx, keypair.Accounts, false)
}
func (db *Database) UpdateKeypairName(keyUID string, name string, clock uint64) error {
func (db *Database) UpdateKeypairName(keyUID string, name string, clock uint64, updateChatAccountName bool) error {
tx, err := db.db.Begin()
if err != nil {
return err
@ -900,8 +985,26 @@ func (db *Database) UpdateKeypairName(keyUID string, name string, clock uint64)
WHERE
key_uid = ?;
`, name, clock, keyUID)
if err != nil {
return err
}
return err
if updateChatAccountName {
_, err = tx.Exec(`
UPDATE
keypairs_accounts
SET
name = ?,
clock = ?
WHERE
key_uid = ?
AND
path = ?;
`, name, clock, keyUID, statusChatPath)
return err
}
return nil
}
func (db *Database) GetWalletAddress() (rst types.Address, err error) {
@ -976,8 +1079,8 @@ func (db *Database) GetNodeConfig() (*params.NodeConfig, error) {
return nodecfg.GetNodeConfigFromDB(db.db)
}
// this doesn't update clock
func (db *Database) UpdateAccountToFullyOperable(keyUID string, address types.Address) (err error) {
// This function should not update the clock, cause it marks accounts locally.
func (db *Database) SetAccountOperability(address types.Address, operable AccountOperable) (err error) {
tx, err := db.db.Begin()
defer func() {
if err == nil {
@ -996,24 +1099,30 @@ func (db *Database) UpdateAccountToFullyOperable(keyUID string, address types.Ad
return err
}
_, err = tx.Exec(`UPDATE keypairs_accounts SET operable = ? WHERE address = ?`, AccountFullyOperable, address)
_, err = tx.Exec(`UPDATE keypairs_accounts SET operable = ? WHERE address = ?`, operable, address)
return err
}
func (db *Database) GetPositionForNextNewAccount() (int64, error) {
var pos sql.NullInt64
err := db.db.QueryRow("SELECT MAX(position) FROM keypairs_accounts").Scan(&pos)
if err != nil {
return 0, err
}
if pos.Valid {
return pos.Int64 + 1, nil
}
return 0, nil
}
// The UpdateAccountPosition function rearranges accounts based on the new position assigned to a given address.
// Please note that this function does not ensure a sequential order
func (db *Database) UpdateAccountPosition(address types.Address, newPosition int64) (err error) {
var maxPosition int64
var minPosition int64
err = db.db.QueryRow("SELECT MAX(position), MIN(position) FROM keypairs_accounts").Scan(&maxPosition, &minPosition)
if err != nil {
return err
}
var currentPosition int64
err = db.db.QueryRow("SELECT position FROM keypairs_accounts where address = ?", address).Scan(&currentPosition)
if err != nil {
return err
}
func (db *Database) UpdateAccountPosition(address types.Address, newPosition int64, clock uint64) (err error) {
var (
maxPosition int64
minPosition int64
currentPosition int64
)
tx, err := db.db.Begin()
defer func() {
if err == nil {
@ -1023,9 +1132,15 @@ func (db *Database) UpdateAccountPosition(address types.Address, newPosition int
_ = tx.Rollback()
}()
err = tx.QueryRow("SELECT MAX(position), MIN(position) FROM keypairs_accounts").Scan(&maxPosition, &minPosition)
if err != nil {
return err
}
acc, err := db.getAccountByAddress(tx, address)
if err != nil {
return err
}
currentPosition = acc.Position
if newPosition == maxPosition {
newPosition++
@ -1047,5 +1162,11 @@ func (db *Database) UpdateAccountPosition(address types.Address, newPosition int
return err
}
// Update keypair clock if any but the watch only account was deleted.
if acc.KeyUID != "" {
err = db.updateKeypairClock(tx, acc.KeyUID, clock)
return err
}
return nil
}

View File

@ -36,7 +36,7 @@ func TestGetAddresses(t *testing.T) {
{Address: types.Address{0x01}, Chat: true, Wallet: true},
{Address: types.Address{0x02}},
}
require.NoError(t, db.SaveOrUpdateAccounts(accounts))
require.NoError(t, db.SaveOrUpdateAccounts(accounts, false))
addresses, err := db.GetAddresses()
require.NoError(t, err)
require.Equal(t, []types.Address{{0x01}, {0x02}}, addresses)
@ -46,16 +46,16 @@ func TestUpdateAccountPosition(t *testing.T) {
db, stop := setupTestDB(t)
defer stop()
accounts := []*Account{
{Address: types.Address{0x01}, Chat: true, Wallet: true},
{Address: types.Address{0x02}},
{Address: types.Address{0x01}, Position: 1},
{Address: types.Address{0x02}, Position: 2},
}
require.NoError(t, db.SaveOrUpdateAccounts(accounts))
require.NoError(t, db.SaveOrUpdateAccounts(accounts, false))
accountsRes, err := db.GetAccounts()
require.NoError(t, err)
require.Equal(t, accountsRes[0].Position, int64(1))
require.Equal(t, accountsRes[1].Position, int64(2))
err = db.UpdateAccountPosition(accounts[1].Address, 1)
err = db.UpdateAccountPosition(accounts[1].Address, 1, 0)
require.NoError(t, err)
accountsRes, err = db.GetAccounts()
@ -70,7 +70,7 @@ func TestGetWalletAddress(t *testing.T) {
address := types.Address{0x01}
_, err := db.GetWalletAddress()
require.Equal(t, err, sql.ErrNoRows)
require.NoError(t, db.SaveOrUpdateAccounts([]*Account{{Address: address, Wallet: true}}))
require.NoError(t, db.SaveOrUpdateAccounts([]*Account{{Address: address, Wallet: true}}, false))
wallet, err := db.GetWalletAddress()
require.NoError(t, err)
require.Equal(t, address, wallet)
@ -82,7 +82,7 @@ func TestGetChatAddress(t *testing.T) {
address := types.Address{0x01}
_, err := db.GetChatAddress()
require.Equal(t, err, sql.ErrNoRows)
require.NoError(t, db.SaveOrUpdateAccounts([]*Account{{Address: address, Chat: true}}))
require.NoError(t, db.SaveOrUpdateAccounts([]*Account{{Address: address, Chat: true}}, false))
chat, err := db.GetChatAddress()
require.NoError(t, err)
require.Equal(t, address, chat)
@ -95,7 +95,7 @@ func TestAddressExists(t *testing.T) {
accounts := []*Account{
{Address: types.Address{0x01}, Chat: true, Wallet: true},
}
require.NoError(t, db.SaveOrUpdateAccounts(accounts))
require.NoError(t, db.SaveOrUpdateAccounts(accounts, false))
exists, err := db.AddressExists(accounts[0].Address)
require.NoError(t, err)
@ -133,7 +133,7 @@ func TestWatchOnlyAccounts(t *testing.T) {
require.Equal(t, 0, len(dbAccounts))
// save watch only accounts
err = db.SaveOrUpdateAccounts(woAccounts)
err = db.SaveOrUpdateAccounts(woAccounts, false)
require.NoError(t, err)
_, err = db.GetKeypairByKeyUID(woAccounts[0].KeyUID)
require.Error(t, err)
@ -144,7 +144,7 @@ func TestWatchOnlyAccounts(t *testing.T) {
require.Equal(t, woAccounts[0].Address, dbAccounts[0].Address)
// try to save the same watch only account again
err = db.SaveOrUpdateAccounts(woAccounts[:1])
err = db.SaveOrUpdateAccounts(woAccounts[:1], false)
require.NoError(t, err)
dbAccounts, err = db.GetAccounts()
require.NoError(t, err)
@ -161,7 +161,7 @@ func TestWatchOnlyAccounts(t *testing.T) {
ColorID: common.CustomizationColorPrimary,
Emoji: "emoji-1",
}
err = db.SaveOrUpdateAccounts([]*Account{wo4})
err = db.SaveOrUpdateAccounts([]*Account{wo4}, false)
require.NoError(t, err)
dbAccounts, err = db.GetAccounts()
require.NoError(t, err)
@ -174,7 +174,7 @@ func TestWatchOnlyAccounts(t *testing.T) {
wo4.Name = wo4.Name + "updated"
wo4.ColorID = common.CustomizationColorCamel
wo4.Emoji = wo4.Emoji + "updated"
err = db.SaveOrUpdateAccounts([]*Account{wo4})
err = db.SaveOrUpdateAccounts([]*Account{wo4}, false)
require.NoError(t, err)
dbAccounts, err = db.GetAccounts()
require.NoError(t, err)
@ -189,7 +189,7 @@ func TestWatchOnlyAccounts(t *testing.T) {
require.True(t, err == ErrDbKeypairNotFound)
// try to delete watch only account
err = db.DeleteAccount(wo4.Address)
err = db.DeleteAccount(wo4.Address, 0)
require.NoError(t, err)
dbAccounts, err = db.GetAccounts()
require.NoError(t, err)
@ -220,7 +220,8 @@ func TestUpdateKeypairName(t *testing.T) {
// update keypair name
kp.Name = kp.Name + "updated"
err = db.UpdateKeypairName(kp.KeyUID, kp.Name, kp.Clock)
kp.Accounts[0].Name = kp.Name
err = db.UpdateKeypairName(kp.KeyUID, kp.Name, kp.Clock, true)
require.NoError(t, err)
// check keypair
@ -288,7 +289,7 @@ func TestKeypairs(t *testing.T) {
accToUpdate := kp.Accounts[ind]
// try to save the same account again
err = db.SaveOrUpdateAccounts([]*Account{accToUpdate})
err = db.SaveOrUpdateAccounts([]*Account{accToUpdate}, false)
require.NoError(t, err)
dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
require.NoError(t, err)
@ -303,7 +304,7 @@ func TestKeypairs(t *testing.T) {
accToUpdate.ColorID = common.CustomizationColorBrown
accToUpdate.Emoji = accToUpdate.Emoji + "updated"
err = db.SaveOrUpdateAccounts([]*Account{accToUpdate})
err = db.SaveOrUpdateAccounts([]*Account{accToUpdate}, false)
require.NoError(t, err)
dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
require.NoError(t, err)
@ -335,7 +336,7 @@ func TestKeypairs(t *testing.T) {
accToAdd.PublicKey = types.Hex2Bytes("0x000000008")
accToAdd.Name = "Generated Acc 8"
err = db.SaveOrUpdateAccounts([]*Account{accToAdd})
err = db.SaveOrUpdateAccounts([]*Account{accToAdd}, false)
require.NoError(t, err)
dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
require.NoError(t, err)
@ -364,7 +365,7 @@ func TestKeypairs(t *testing.T) {
expectedLastUsedDerivationIndex = 1
}
err = db.SaveOrUpdateAccounts([]*Account{accToAdd})
err = db.SaveOrUpdateAccounts([]*Account{accToAdd}, false)
require.NoError(t, err)
dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
require.NoError(t, err)
@ -378,7 +379,7 @@ func TestKeypairs(t *testing.T) {
require.Equal(t, accToAdd.Address, dbAcc.Address)
// delete account
err = db.DeleteAccount(accToAdd.Address)
err = db.DeleteAccount(accToAdd.Address, 0)
require.NoError(t, err)
dbAccounts, err = db.GetAccounts()
require.NoError(t, err)
@ -388,7 +389,7 @@ func TestKeypairs(t *testing.T) {
require.True(t, err == ErrDbAccountNotFound)
for _, acc := range dbAccounts {
err = db.DeleteAccount(acc.Address)
err = db.DeleteAccount(acc.Address, 0)
require.NoError(t, err)
}

View File

@ -120,7 +120,60 @@ func (kp *Keycards) processResult(rows *sql.Rows, groupByKeycard bool) ([]*Keyca
return keycards, nil
}
func (kp *Keycards) getAllRows(groupByKeycard bool) ([]*Keycard, error) {
func (kp *Keycards) getAllRows(tx *sql.Tx, groupByKeycard bool) ([]*Keycard, error) {
var (
rows *sql.Rows
err error
)
query := // nolint: gosec
`
SELECT
k.keycard_uid,
k.keycard_name,
k.keycard_locked,
ka.account_address,
k.key_uid,
k.last_update_clock
FROM
keycards AS k
LEFT JOIN
keycards_accounts AS ka
ON
k.keycard_uid = ka.keycard_uid
ORDER BY
key_uid`
if tx == nil {
rows, err = kp.db.Query(query)
if err != nil {
return nil, err
}
} else {
stmt, err := tx.Prepare(query)
if err != nil {
return nil, err
}
defer stmt.Close()
rows, err = stmt.Query()
if err != nil {
return nil, err
}
}
defer rows.Close()
return kp.processResult(rows, groupByKeycard)
}
func (kp *Keycards) GetAllKnownKeycards() ([]*Keycard, error) {
return kp.getAllRows(nil, true)
}
func (kp *Keycards) GetAllKnownKeycardsGroupedByKeyUID() ([]*Keycard, error) {
return kp.getAllRows(nil, false)
}
func (kp *Keycards) GetKeycardByKeyUID(keyUID string) ([]*Keycard, error) {
rows, err := kp.db.Query(`
SELECT
k.keycard_uid,
@ -130,44 +183,10 @@ func (kp *Keycards) getAllRows(groupByKeycard bool) ([]*Keycard, error) {
k.key_uid,
k.last_update_clock
FROM
keycards AS k
LEFT JOIN
keycards_accounts AS ka
ON
k.keycard_uid = ka.keycard_uid
ORDER BY
key_uid
`)
if err != nil {
return nil, err
}
defer rows.Close()
return kp.processResult(rows, groupByKeycard)
}
func (kp *Keycards) GetAllKnownKeycards() ([]*Keycard, error) {
return kp.getAllRows(true)
}
func (kp *Keycards) GetAllKnownKeycardsGroupedByKeyUID() ([]*Keycard, error) {
return kp.getAllRows(false)
}
func (kp *Keycards) GetKeycardByKeyUID(keyUID string) ([]*Keycard, error) {
rows, err := kp.db.Query(`
SELECT
k.keycard_uid,
k.keycard_name,
k.keycard_locked,
ka.account_address,
k.key_uid,
k.last_update_clock
FROM
keycards AS k
LEFT JOIN
keycards_accounts AS ka
ON
keycards AS k
LEFT JOIN
keycards_accounts AS ka
ON
k.keycard_uid = ka.keycard_uid
WHERE
k.key_uid = ?
@ -205,11 +224,11 @@ func (kp *Keycards) setLastUpdateClock(tx *sql.Tx, kcUID string, clock uint64) (
}
_, err = tx.Exec(`
UPDATE
keycards
SET
last_update_clock = ?
WHERE
UPDATE
keycards
SET
last_update_clock = ?
WHERE
keycard_uid = ?`,
clock, kcUID)

View File

@ -11,7 +11,7 @@ func MockTestAccounts(t *testing.T, db *sql.DB, accounts []*Account) {
d, err := NewDB(db)
require.NoError(t, err)
err = d.SaveOrUpdateAccounts(accounts)
err = d.SaveOrUpdateAccounts(accounts, false)
require.NoError(t, err)
res, err := d.GetAccounts()
require.NoError(t, err)

View File

@ -55,6 +55,10 @@ func (m *AccountManagerMock) Sign(rpcParams account.SignParams, verifiedAccount
return types.HexBytes{}, nil
}
func (m *AccountManagerMock) DeleteAccount(address types.Address) error {
return nil
}
type TokenManagerMock struct {
Balances *map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big
}

View File

@ -2509,7 +2509,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string,
if syncedFromLocalPairing {
kp.SyncedFrom = accounts.SyncedFromLocalPairing
}
err = m.syncKeypair(kp, true, rawMessageHandler)
err = m.syncKeypair(kp, rawMessageHandler)
if err != nil {
return err
}
@ -4269,21 +4269,6 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
allMessagesProcessed = false
continue
}
case protobuf.SyncKeypairFull:
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
logger.Warn("not coming from us, ignoring")
continue
}
p := msg.ParsedMessage.Interface().(protobuf.SyncKeypairFull)
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
logger.Debug("Handling SyncKeypairFull", zap.Any("message", p))
err = m.HandleSyncKeypairFull(messageState, p)
if err != nil {
logger.Warn("failed to handle SyncKeypairFull", zap.Error(err))
allMessagesProcessed = false
continue
}
case protobuf.SyncAccount:
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
logger.Warn("not coming from us, ignoring")
@ -4293,7 +4278,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
p := msg.ParsedMessage.Interface().(protobuf.SyncAccount)
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
logger.Debug("Handling SyncAccount", zap.Any("message", p))
err = m.HandleSyncWalletAccount(messageState, p, "")
err = m.HandleSyncWatchOnlyAccount(messageState, p)
if err != nil {
logger.Warn("failed to handle SyncAccount", zap.Error(err))
allMessagesProcessed = false

View File

@ -96,7 +96,7 @@ func (m *Messenger) BackupData(ctx context.Context) (uint64, error) {
return 0, errors[0]
}
fullKeypairsToBackup, err := m.backupKeypairs()
keypairsToBackup, err := m.backupKeypairs()
if err != nil {
return 0, err
}
@ -125,9 +125,9 @@ func (m *Messenger) BackupData(ctx context.Context) (uint64, error) {
DataNumber: uint32(0),
TotalNumber: uint32(len(settings)),
},
FullKeypairDetails: &protobuf.FetchingBackedUpDataDetails{
KeypairDetails: &protobuf.FetchingBackedUpDataDetails{
DataNumber: uint32(0),
TotalNumber: uint32(len(fullKeypairsToBackup)),
TotalNumber: uint32(len(keypairsToBackup)),
},
WatchOnlyAccountDetails: &protobuf.FetchingBackedUpDataDetails{
DataNumber: uint32(0),
@ -181,10 +181,10 @@ func (m *Messenger) BackupData(ctx context.Context) (uint64, error) {
}
// Update keypairs messages encode and dispatch
for i, d := range fullKeypairsToBackup {
for i, d := range keypairsToBackup {
pb := backupDetailsOnly()
pb.FullKeypairDetails.DataNumber = uint32(i + 1)
pb.FullKeypair = d.FullKeypair
pb.KeypairDetails.DataNumber = uint32(i + 1)
pb.Keypair = d.Keypair
err = m.encodeAndDispatchBackupMessage(ctx, pb, chat.ID)
if err != nil {
return 0, err
@ -430,13 +430,13 @@ func (m *Messenger) backupKeypairs() ([]*protobuf.Backup, error) {
for _, kp := range keypairs {
kp.SyncedFrom = accounts.SyncedFromBackup
fullKeypair, err := m.prepareSyncKeypairFullMessage(kp)
keypair, err := m.prepareSyncKeypairMessage(kp)
if err != nil {
return nil, err
}
backupMessage := &protobuf.Backup{
FullKeypair: fullKeypair,
Keypair: keypair,
}
backupMessages = append(backupMessages, backupMessage)

View File

@ -3,7 +3,6 @@ package protocol
import (
"database/sql"
"github.com/status-im/status-go/multiaccounts/accounts"
ensservice "github.com/status-im/status-go/services/ens"
"github.com/status-im/status-go/protocol/identity"
@ -50,7 +49,7 @@ func (m *Messenger) HandleBackup(state *ReceivedMessageState, message protobuf.B
errors = append(errors, err)
}
err = m.handleFullKeypair(message.FullKeypair)
err = m.handleKeypair(message.Keypair)
if err != nil {
errors = append(errors, err)
}
@ -70,7 +69,7 @@ func (m *Messenger) HandleBackup(state *ReceivedMessageState, message protobuf.B
response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyContacts, message.ContactsDetails)
response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyCommunities, message.CommunitiesDetails)
response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeySettings, message.SettingsDetails)
response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyKeypairs, message.FullKeypairDetails)
response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyKeypairs, message.KeypairDetails)
response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyWatchOnlyAccounts, message.WatchOnlyAccountDetails)
m.config.messengerSignalsHandler.SendWakuFetchingBackupProgress(&response)
@ -197,13 +196,16 @@ func (m *Messenger) handleBackedUpSettings(message *protobuf.SyncSetting) error
return nil
}
func (m *Messenger) handleFullKeypair(message *protobuf.SyncKeypairFull) error {
func (m *Messenger) handleKeypair(message *protobuf.SyncKeypair) error {
if message == nil {
return nil
}
keypair, keycards, err := m.handleSyncKeypairFull(message)
keypair, err := m.handleSyncKeypair(message)
if err != nil {
if err == ErrTryingToStoreOldKeypair {
return nil
}
return err
}
@ -213,12 +215,6 @@ func (m *Messenger) handleFullKeypair(message *protobuf.SyncKeypairFull) error {
}
m.config.messengerSignalsHandler.SendWakuBackedUpKeypair(&kpResponse)
kcResponse := wakusync.WakuBackedUpDataResponse{
Keycards: keycards,
}
m.config.messengerSignalsHandler.SendWakuBackedUpKeycards(&kcResponse)
}
return nil
@ -229,8 +225,11 @@ func (m *Messenger) handleWatchOnlyAccount(message *protobuf.SyncAccount) error
return nil
}
acc, err := m.handleSyncWalletAccount(message, accounts.SyncedFromBackup)
acc, err := m.handleSyncWatchOnlyAccount(message)
if err != nil {
if err == ErrTryingToStoreOldWalletAccount {
return nil
}
return err
}

View File

@ -757,9 +757,6 @@ func (s *MessengerBackupSuite) TestBackupKeypairs() {
s.Require().Equal(accounts.SyncedFromBackup, dbProfileKp2.SyncedFrom)
for _, acc := range profileKp.Accounts {
if acc.Chat {
continue
}
s.Require().True(contains(dbProfileKp2.Accounts, acc, accounts.SameAccounts))
}
@ -849,7 +846,7 @@ func (s *MessengerBackupSuite) TestBackupWatchOnlyAccounts() {
bob1 := s.m
woAccounts := accounts.GetWatchOnlyAccountsForTest()
err := bob1.settings.SaveOrUpdateAccounts(woAccounts)
err := bob1.settings.SaveOrUpdateAccounts(woAccounts, false)
s.Require().NoError(err)
dbWoAccounts1, err := bob1.settings.GetWatchOnlyAccounts()
s.Require().NoError(err)

View File

@ -58,7 +58,6 @@ type MessengerSignalsHandler interface {
SendWakuBackedUpProfile(response *wakusync.WakuBackedUpDataResponse)
SendWakuBackedUpSettings(response *wakusync.WakuBackedUpDataResponse)
SendWakuBackedUpKeypair(response *wakusync.WakuBackedUpDataResponse)
SendWakuBackedUpKeycards(response *wakusync.WakuBackedUpDataResponse)
SendWakuBackedUpWatchOnlyAccount(response *wakusync.WakuBackedUpDataResponse)
}

View File

@ -42,9 +42,10 @@ const (
var (
ErrMessageNotAllowed = errors.New("message from a non-contact")
ErrMessageForWrongChatType = errors.New("message for the wrong chat type")
ErrNotWalletAccount = errors.New("an account is not a wallet account")
ErrNotWatchOnlyAccount = errors.New("an account is not a watch only account")
ErrWalletAccountNotSupportedForMobileApp = errors.New("handling account is not supported for mobile app")
ErrTryingToStoreOldWalletAccount = errors.New("trying to store an old wallet account")
ErrTryingToStoreOldKeypair = errors.New("trying to store an old keypair")
ErrSomeFieldsMissingForWalletAccount = errors.New("some fields are missing for wallet account")
ErrTryingToRemoveUnexistingWalletAccount = errors.New("trying to remove an unexisting wallet account")
ErrUnknownKeypairForWalletAccount = errors.New("keypair is not known for the wallet account")
@ -2979,11 +2980,13 @@ func (m *Messenger) updateUnviewedCounts(chat *Chat, mentionedOrReplied bool) {
chat.UnviewedMentionsCount++
}
}
func mapSyncAccountToAccount(message *protobuf.SyncAccount, accountOperability accounts.AccountOperable) *accounts.Account {
func mapSyncAccountToAccount(message *protobuf.SyncAccount, accountOperability accounts.AccountOperable, accType accounts.AccountType) *accounts.Account {
return &accounts.Account{
Address: types.BytesToAddress(message.Address),
KeyUID: message.KeyUid,
PublicKey: types.HexBytes(message.PublicKey),
Type: accType,
Path: message.Path,
Name: message.Name,
ColorID: multiaccountscommon.CustomizationColor(message.ColorId),
@ -2993,24 +2996,40 @@ func mapSyncAccountToAccount(message *protobuf.SyncAccount, accountOperability a
Hidden: message.Hidden,
Clock: message.Clock,
Operable: accountOperability,
Removed: message.Removed,
Position: message.Position,
}
}
func (m *Messenger) resolveAccountOperability(keyUID string, defaultWalletAccount bool, accountReceivedFromRecovering bool) (accounts.AccountOperable, error) {
knownKeycardsForKeyUID, err := m.settings.GetKeycardByKeyUID(keyUID)
if err != nil {
if err == accounts.ErrDbKeypairNotFound {
return accounts.AccountNonOperable, nil
}
return accounts.AccountNonOperable, err
func (m *Messenger) resolveAccountOperability(syncAcc *protobuf.SyncAccount, syncKpMigratedToKeycard bool, accountReceivedFromLocalPairing bool) (accounts.AccountOperable, error) {
accountsOperability := accounts.AccountNonOperable
dbAccount, err := m.settings.GetAccountByAddress(types.BytesToAddress(syncAcc.Address))
if err != nil && err != accounts.ErrDbAccountNotFound {
return accountsOperability, err
}
if dbAccount != nil {
return dbAccount.Operable, nil
}
keypairMigratedToKeycard := len(knownKeycardsForKeyUID) > 0
accountsOperability := accounts.AccountNonOperable
if keypairMigratedToKeycard || accountReceivedFromRecovering || defaultWalletAccount {
if !syncKpMigratedToKeycard {
// We're here when we receive a keypair from the paired device which is either:
// 1. regular keypair or
// 2. was just converted from keycard to a regular keypair.
dbKeycardsForKeyUID, err := m.settings.GetKeycardByKeyUID(syncAcc.KeyUid)
if err != nil && err != accounts.ErrDbKeypairNotFound {
return accounts.AccountNonOperable, err
}
if len(dbKeycardsForKeyUID) > 0 {
// We're here in 2. case from above and in this case we need to mark all accounts for this keypair non operable
return accounts.AccountNonOperable, nil
}
}
if syncKpMigratedToKeycard || accountReceivedFromLocalPairing || syncAcc.Chat || syncAcc.Wallet {
accountsOperability = accounts.AccountFullyOperable
} else {
partiallyOrFullyOperable, err := m.settings.IsAnyAccountPartalyOrFullyOperableForKeyUID(keyUID)
partiallyOrFullyOperable, err := m.settings.IsAnyAccountPartiallyOrFullyOperableForKeyUID(syncAcc.KeyUid)
if err != nil {
if err == accounts.ErrDbKeypairNotFound {
return accounts.AccountNonOperable, nil
@ -3025,30 +3044,13 @@ func (m *Messenger) resolveAccountOperability(keyUID string, defaultWalletAccoun
return accountsOperability, nil
}
func (m *Messenger) handleSyncWalletAccount(message *protobuf.SyncAccount, syncedFrom string) (*accounts.Account, error) {
if message.Chat {
return nil, ErrNotWalletAccount
func (m *Messenger) handleSyncWatchOnlyAccount(message *protobuf.SyncAccount) (*accounts.Account, error) {
if message.KeyUid != "" {
return nil, ErrNotWatchOnlyAccount
}
accountOperability := accounts.AccountFullyOperable
// The only account without `KeyUid` is watch only account and it doesn't belong to any keypair.
if message.KeyUid != "" {
_, err := m.settings.GetKeypairByKeyUID(message.KeyUid)
if err != nil {
if err == accounts.ErrDbKeypairNotFound {
return nil, ErrUnknownKeypairForWalletAccount
}
return nil, err
}
accountReceivedFromRecovering := syncedFrom == accounts.SyncedFromLocalPairing
accountOperability, err = m.resolveAccountOperability(message.KeyUid, message.Wallet, accountReceivedFromRecovering)
if err != nil {
return nil, err
}
}
accAddress := types.BytesToAddress(message.Address)
dbAccount, err := m.settings.GetAccountByAddress(accAddress)
if err != nil && err != accounts.ErrDbAccountNotFound {
@ -3061,7 +3063,7 @@ func (m *Messenger) handleSyncWalletAccount(message *protobuf.SyncAccount, synce
}
if message.Removed {
err = m.settings.DeleteAccount(accAddress)
err = m.settings.DeleteAccount(accAddress, message.Clock)
dbAccount.Removed = true
return dbAccount, err
}
@ -3071,9 +3073,9 @@ func (m *Messenger) handleSyncWalletAccount(message *protobuf.SyncAccount, synce
}
}
acc := mapSyncAccountToAccount(message, accountOperability)
acc := mapSyncAccountToAccount(message, accountOperability, accounts.AccountTypeWatch)
err = m.settings.SaveOrUpdateAccounts([]*accounts.Account{acc})
err = m.settings.SaveOrUpdateAccounts([]*accounts.Account{acc}, false)
if err != nil {
return nil, err
}
@ -3098,95 +3100,98 @@ func (m *Messenger) handleSyncKeypair(message *protobuf.SyncKeypair) (*accounts.
LastUsedDerivationIndex: message.LastUsedDerivationIndex,
SyncedFrom: message.SyncedFrom,
Clock: message.Clock,
Removed: message.Removed,
}
saveOrUpdate := dbKeypair == nil
accountReceivedFromLocalPairing := message.SyncedFrom == accounts.SyncedFromLocalPairing
if dbKeypair != nil {
saveOrUpdate = dbKeypair.Clock < kp.Clock
if dbKeypair.Clock >= kp.Clock {
return nil, ErrTryingToStoreOldKeypair
}
// in case of keypair update, we need to keep `synced_from` field as it was when keypair was introduced to this device for the first time
kp.SyncedFrom = dbKeypair.SyncedFrom
}
if saveOrUpdate {
accountReceivedFromRecovering := message.SyncedFrom == accounts.SyncedFromLocalPairing
if dbKeypair != nil && message.SyncedFrom == accounts.SyncedFromBackup {
// in case of recovering from backed up messages we need to delete messages which are already in db (eg. stored py previously received backed up message)
for _, dbAcc := range dbKeypair.Accounts {
found := false
for _, sAcc := range message.Accounts {
sAccAddress := types.BytesToAddress(sAcc.Address)
if dbAcc.Address == sAccAddress {
found = true
break
}
}
if found {
err = m.settings.DeleteAccount(dbAcc.Address)
if err != nil {
return nil, err
}
}
}
}
for _, sAcc := range message.Accounts {
accountOperability, err := m.resolveAccountOperability(sAcc.KeyUid, sAcc.Wallet, accountReceivedFromRecovering)
if err != nil {
return nil, err
}
acc := mapSyncAccountToAccount(sAcc, accountOperability)
kp.Accounts = append(kp.Accounts, acc)
}
err = m.settings.SaveOrUpdateKeypair(kp)
if err != nil {
return nil, err
} else {
if kp.Removed {
return nil, nil
}
}
for _, sAcc := range message.Accounts {
acc, err := m.handleSyncWalletAccount(sAcc, message.SyncedFrom)
syncKpMigratedToKeycard := len(message.Keycards) > 0
accountOperability, err := m.resolveAccountOperability(sAcc, syncKpMigratedToKeycard, accountReceivedFromLocalPairing)
if err != nil {
if err == ErrNotWalletAccount ||
err == ErrTryingToStoreOldWalletAccount ||
err == ErrTryingToRemoveUnexistingWalletAccount {
continue
}
return nil, err
}
acc := mapSyncAccountToAccount(sAcc, accountOperability, accounts.GetAccountTypeForKeypairType(kp.Type))
kp.Accounts = append(kp.Accounts, acc)
}
return kp, nil
}
if kp.Removed {
// delete all keystore files
for _, dbAcc := range dbKeypair.Accounts {
err = m.deleteKeystoreFileForAddress(dbAcc.Address)
if err != nil {
return nil, err
}
}
} else if !accountReceivedFromLocalPairing && dbKeypair != nil {
for _, dbAcc := range dbKeypair.Accounts {
found := false
for _, acc := range kp.Accounts {
if dbAcc.Address == acc.Address {
found = true
break
}
}
if !found {
err = m.deleteKeystoreFileForAddress(dbAcc.Address)
if err != nil {
return nil, err
}
}
}
}
func (m *Messenger) handleSyncKeypairFull(message *protobuf.SyncKeypairFull) (kp *accounts.Keypair, keycards []*accounts.Keycard, err error) {
kp, err = m.handleSyncKeypair(message.Keypair)
err = m.settings.DeleteKeypair(message.KeyUid)
if err != nil && err != accounts.ErrDbKeypairNotFound {
return nil, err
}
// if entire keypair was removed, there is no point to continue
if kp.Removed {
return kp, nil
}
err = m.settings.SaveOrUpdateKeypair(kp)
if err != nil {
return nil, nil, err
return nil, err
}
for _, sKc := range message.Keycards {
kc := accounts.Keycard{}
kc.FromSyncKeycard(sKc)
keycards = append(keycards, &kc)
kp.Keycards = append(kp.Keycards, &kc)
}
err = m.settings.ApplyKeycardsForKeypairWithKeyUID(kp.KeyUID, keycards)
err = m.settings.ApplyKeycardsForKeypairWithKeyUID(kp.KeyUID, kp.Keycards)
if err != nil {
return kp, keycards, err
return nil, err
}
return
return kp, nil
}
func (m *Messenger) HandleSyncWalletAccount(state *ReceivedMessageState, message protobuf.SyncAccount, syncedFrom string) error {
acc, err := m.handleSyncWalletAccount(&message, syncedFrom)
func (m *Messenger) HandleSyncWatchOnlyAccount(state *ReceivedMessageState, message protobuf.SyncAccount) error {
acc, err := m.handleSyncWatchOnlyAccount(&message)
if err != nil {
if err == ErrTryingToStoreOldWalletAccount {
return nil
}
return err
}
state.Response.Accounts = append(state.Response.Accounts, acc)
state.Response.WatchOnlyAccounts = append(state.Response.WatchOnlyAccounts, acc)
return nil
}
@ -3194,6 +3199,9 @@ func (m *Messenger) HandleSyncWalletAccount(state *ReceivedMessageState, message
func (m *Messenger) HandleSyncKeypair(state *ReceivedMessageState, message protobuf.SyncKeypair) error {
kp, err := m.handleSyncKeypair(&message)
if err != nil {
if err == ErrTryingToStoreOldKeypair {
return nil
}
return err
}
@ -3202,18 +3210,6 @@ func (m *Messenger) HandleSyncKeypair(state *ReceivedMessageState, message proto
return nil
}
func (m *Messenger) HandleSyncKeypairFull(state *ReceivedMessageState, message protobuf.SyncKeypairFull) error {
keypair, keycards, err := m.handleSyncKeypairFull(&message)
if err != nil {
return err
}
state.Response.Keypairs = append(state.Response.Keypairs, keypair)
state.Response.Keycards = append(state.Response.Keycards, keycards...)
return nil
}
func (m *Messenger) HandleSyncContactRequestDecision(state *ReceivedMessageState, message protobuf.SyncContactRequestDecision) error {
var err error
var response *MessengerResponse

View File

@ -45,7 +45,7 @@ type MessengerResponse struct {
Bookmarks []*browsers.Bookmark
Settings []*settings.SyncSettingField
IdentityImages []images.IdentityImage
Accounts []*accounts.Account
WatchOnlyAccounts []*accounts.Account
Keypairs []*accounts.Keypair
DiscordCategories []*discord.Category
DiscordChannels []*discord.Channel
@ -74,7 +74,6 @@ type MessengerResponse struct {
trustStatus map[string]verification.TrustStatus
emojiReactions map[string]*EmojiReaction
savedAddresses map[string]*wallet.SavedAddress
Keycards []*accounts.Keycard
keycardActions []*accounts.KeycardAction
SocialLinksInfo *identity.SocialLinksInfo
ensUsernameDetails []*ensservice.UsernameDetail
@ -109,7 +108,7 @@ func (r *MessengerResponse) MarshalJSON() ([]byte, error) {
StatusUpdates []UserStatus `json:"statusUpdates,omitempty"`
Settings []*settings.SyncSettingField `json:"settings,omitempty"`
IdentityImages []images.IdentityImage `json:"identityImages,omitempty"`
Accounts []*accounts.Account `json:"accounts,omitempty"`
WatchOnlyAccounts []*accounts.Account `json:"watchOnlyAccounts,omitempty"`
Keypairs []*accounts.Keypair `json:"keypairs,omitempty"`
DiscordCategories []*discord.Category `json:"discordCategories,omitempty"`
DiscordChannels []*discord.Channel `json:"discordChannels,omitempty"`
@ -117,7 +116,6 @@ 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 []*accounts.Keycard `json:"keycards,omitempty"`
KeycardActions []*accounts.KeycardAction `json:"keycardActions,omitempty"`
SocialLinksInfo *identity.SocialLinksInfo `json:"socialLinksInfo,omitempty"`
EnsUsernameDetails []*ensservice.UsernameDetail `json:"ensUsernameDetails,omitempty"`
@ -132,7 +130,7 @@ func (r *MessengerResponse) MarshalJSON() ([]byte, error) {
CurrentStatus: r.currentStatus,
Settings: r.Settings,
IdentityImages: r.IdentityImages,
Accounts: r.Accounts,
WatchOnlyAccounts: r.WatchOnlyAccounts,
Keypairs: r.Keypairs,
Messages: r.Messages(),
@ -153,7 +151,6 @@ func (r *MessengerResponse) MarshalJSON() ([]byte, error) {
DiscordCategories: r.DiscordCategories,
DiscordChannels: r.DiscordChannels,
DiscordOldestMessageTimestamp: r.DiscordOldestMessageTimestamp,
Keycards: r.Keycards,
KeycardActions: r.KeycardActions(),
SocialLinksInfo: r.SocialLinksInfo,
EnsUsernameDetails: r.EnsUsernameDetails(),
@ -273,7 +270,7 @@ func (r *MessengerResponse) IsEmpty() bool {
len(r.removedMessages)+
len(r.Mailservers)+
len(r.IdentityImages)+
len(r.Accounts)+
len(r.WatchOnlyAccounts)+
len(r.Keypairs)+
len(r.notifications)+
len(r.statusUpdates)+
@ -282,7 +279,6 @@ func (r *MessengerResponse) IsEmpty() bool {
len(r.verificationRequests)+
len(r.RequestsToJoinCommunity)+
len(r.savedAddresses)+
len(r.Keycards)+
len(r.keycardActions) == 0 &&
len(r.ensUsernameDetails) == 0 &&
r.currentStatus == nil &&
@ -322,9 +318,8 @@ func (r *MessengerResponse) Merge(response *MessengerResponse) error {
r.AddBookmarks(response.GetBookmarks())
r.CommunityChanges = append(r.CommunityChanges, response.CommunityChanges...)
r.BackupHandled = response.BackupHandled
r.Accounts = append(r.Accounts, response.Accounts...)
r.WatchOnlyAccounts = append(r.WatchOnlyAccounts, response.WatchOnlyAccounts...)
r.Keypairs = append(r.Keypairs, response.Keypairs...)
r.Keycards = append(r.Keycards, response.Keycards...)
r.SocialLinksInfo = response.SocialLinksInfo
return nil

View File

@ -116,296 +116,338 @@ func (s *MessengerSyncKeycardsStateSuite) newMessenger(shh types.Waku) *Messenge
return messenger
}
func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasNoKeycards() {
senderDb := s.main.settings
dbOnReceiver := s.other.settings
// TODO: commented tests below will be handled in the seocnd step of planned improvements
keycard1 := accounts.GetProfileKeycardForTest()
// func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasNoKeycards() {
// senderDb := s.main.settings
// dbOnReceiver := s.other.settings
keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// keycard1 := accounts.GetProfileKeycardForTest()
keycard2Copy := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycard2Copy.KeycardUID = keycard2Copy.KeycardUID + "C"
keycard2Copy.KeycardName = keycard2Copy.KeycardName + "Copy"
keycard2Copy.LastUpdateClock = keycard2Copy.LastUpdateClock + 1
// keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycard3 := accounts.GetKeycardForSeedImportedKeypair2ForTest()
// keycard2Copy := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// keycard2Copy.KeycardUID = keycard2Copy.KeycardUID + "C"
// keycard2Copy.KeycardName = keycard2Copy.KeycardName + "Copy"
// keycard2Copy.LastUpdateClock = keycard2Copy.LastUpdateClock + 1
// Add keycards on sender
addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2Copy)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard3)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
// keycard3 := accounts.GetKeycardForSeedImportedKeypair2ForTest()
// Trigger's a sync between devices
err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
s.Require().NoError(err)
// // Add keycards on sender
// addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2Copy)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard3)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// Wait for the response
_, err = WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
return len(r.Keypairs) == 3 && len(r.Keycards) == 4
},
"expected to receive keycards",
)
s.Require().NoError(err)
// // Trigger's a sync between devices
// err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
// s.Require().NoError(err)
syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
s.Require().NoError(err)
s.Require().Equal(4, len(syncedKeycards))
s.Require().True(contains(syncedKeycards, keycard1, accounts.SameKeycards))
s.Require().True(contains(syncedKeycards, keycard2, accounts.SameKeycards))
s.Require().True(contains(syncedKeycards, keycard2Copy, accounts.SameKeycards))
s.Require().True(contains(syncedKeycards, keycard3, accounts.SameKeycards))
}
// // Wait for the response
// _, err = WaitOnMessengerResponse(
// s.other,
// func(r *MessengerResponse) bool {
// success := len(r.Keypairs) == 3
// for _, kp := range r.Keypairs {
// if kp.KeyUID == keycard1.KeyUID {
// success = success && len(kp.Keycards) == 1
// } else if kp.KeyUID == keycard2.KeyUID {
// success = success && len(kp.Keycards) == 2
// } else if kp.KeyUID == keycard3.KeyUID {
// success = success && len(kp.Keycards) == 1
// }
// }
// return success
// },
// "expected to receive keycards",
// )
// s.Require().NoError(err)
func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasKeycardsOlderThanSender() {
senderDb := s.main.settings
dbOnReceiver := s.other.settings
// syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
// s.Require().NoError(err)
// s.Require().Equal(4, len(syncedKeycards))
// s.Require().True(contains(syncedKeycards, keycard1, accounts.SameKeycards))
// s.Require().True(contains(syncedKeycards, keycard2, accounts.SameKeycards))
// s.Require().True(contains(syncedKeycards, keycard2Copy, accounts.SameKeycards))
// s.Require().True(contains(syncedKeycards, keycard3, accounts.SameKeycards))
// }
keycard1 := accounts.GetProfileKeycardForTest()
// func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasKeycardsOlderThanSender() {
// senderDb := s.main.settings
// dbOnReceiver := s.other.settings
keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// keycard1 := accounts.GetProfileKeycardForTest()
// Add keycards on sender
addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
// keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// Add keycards on receiver - partially
keycardR1 := accounts.GetProfileKeycardForTest()
keycardR1.KeycardName = "CardNameToBeChanged-0"
keycardR1.AccountsAddresses = keycardR1.AccountsAddresses[2:3]
keycardR1.LastUpdateClock = keycardR1.LastUpdateClock - 1
// // Add keycards on sender
// addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
keycardR2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycardR2.KeycardName = "CardNameToBeChanged-1"
keycardR2.LastUpdateClock = keycardR2.LastUpdateClock - 1
// // Add keycards on receiver - partially
// keycardR1 := accounts.GetProfileKeycardForTest()
// keycardR1.KeycardName = "CardNameToBeChanged-0"
// keycardR1.AccountsAddresses = keycardR1.AccountsAddresses[2:3]
// keycardR1.LastUpdateClock = keycardR1.LastUpdateClock - 1
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycardR1)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycardR2)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
// keycardR2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// keycardR2.KeycardName = "CardNameToBeChanged-1"
// keycardR2.LastUpdateClock = keycardR2.LastUpdateClock - 1
// Trigger's a sync between devices
err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
s.Require().NoError(err)
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycardR1)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycardR2)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// Wait for the response
_, err = WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
return len(r.Keypairs) == 3 && len(r.Keycards) == 2
},
"expected to receive keycards",
)
s.Require().NoError(err)
// // Trigger's a sync between devices
// err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
// s.Require().NoError(err)
syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
s.Require().NoError(err)
s.Require().Equal(2, len(syncedKeycards))
s.Require().True(contains(syncedKeycards, keycard1, accounts.SameKeycards))
s.Require().True(contains(syncedKeycards, keycard2, accounts.SameKeycards))
}
// // Wait for the response
// _, err = WaitOnMessengerResponse(
// s.other,
// func(r *MessengerResponse) bool {
// success := len(r.Keypairs) == 3
// for _, kp := range r.Keypairs {
// if kp.KeyUID == keycardR1.KeyUID {
// success = success && len(kp.Keycards) == 1
// } else if kp.KeyUID == keycardR2.KeyUID {
// success = success && len(kp.Keycards) == 1
// }
// }
// return success
// },
// "expected to receive keycards",
// )
// s.Require().NoError(err)
func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasKeycardsNewerThanSender() {
senderDb := s.main.settings
dbOnReceiver := s.other.settings
// syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
// s.Require().NoError(err)
// s.Require().Equal(2, len(syncedKeycards))
// s.Require().True(contains(syncedKeycards, keycard1, accounts.SameKeycards))
// s.Require().True(contains(syncedKeycards, keycard2, accounts.SameKeycards))
// }
keycard1 := accounts.GetProfileKeycardForTest()
// func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverHasKeycardsNewerThanSender() {
// senderDb := s.main.settings
// dbOnReceiver := s.other.settings
keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// keycard1 := accounts.GetProfileKeycardForTest()
// Add keycards on sender
addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
// keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// Add keycards on receiver - partially
keycardR1 := accounts.GetProfileKeycardForTest()
keycardR1.KeycardName = "CardNameToBeChanged-0"
keycardR1.AccountsAddresses = keycardR1.AccountsAddresses[2:3]
keycardR1.LastUpdateClock = keycardR1.LastUpdateClock + 1
// // Add keycards on sender
// addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
keycardR2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycardR2.KeycardName = "CardNameToBeChanged-1"
keycardR2.LastUpdateClock = keycardR2.LastUpdateClock + 1
// // Add keycards on receiver - partially
// keycardR1 := accounts.GetProfileKeycardForTest()
// keycardR1.KeycardName = "CardNameToBeChanged-0"
// keycardR1.AccountsAddresses = keycardR1.AccountsAddresses[2:3]
// keycardR1.LastUpdateClock = keycardR1.LastUpdateClock + 1
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycardR1)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycardR2)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
// keycardR2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// keycardR2.KeycardName = "CardNameToBeChanged-1"
// keycardR2.LastUpdateClock = keycardR2.LastUpdateClock + 1
// Trigger's a sync between devices
err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
s.Require().NoError(err)
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycardR1)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycardR2)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// Wait for the response
_, err = WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
return len(r.Keypairs) == 3 && len(r.Keycards) == 2
},
"expected to receive keycards",
)
s.Require().NoError(err)
// // Trigger's a sync between devices
// err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
// s.Require().NoError(err)
syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
s.Require().NoError(err)
s.Require().Equal(2, len(syncedKeycards))
s.Require().True(contains(syncedKeycards, keycardR1, accounts.SameKeycards))
s.Require().True(contains(syncedKeycards, keycardR2, accounts.SameKeycards))
}
// // Wait for the response
// _, err = WaitOnMessengerResponse(
// s.other,
// func(r *MessengerResponse) bool {
// success := len(r.Keypairs) == 3
// for _, kp := range r.Keypairs {
// if kp.KeyUID == keycardR1.KeyUID {
// success = success && len(kp.Keycards) == 1
// } else if kp.KeyUID == keycardR2.KeyUID {
// success = success && len(kp.Keycards) == 1
// }
// }
// return success
// },
// "expected to receive keycards",
// )
// s.Require().NoError(err)
func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfKeycardsWereDeletedOnSenderSide() {
senderDb := s.main.settings
dbOnReceiver := s.other.settings
// syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
// s.Require().NoError(err)
// s.Require().Equal(2, len(syncedKeycards))
// s.Require().True(contains(syncedKeycards, keycardR1, accounts.SameKeycards))
// s.Require().True(contains(syncedKeycards, keycardR2, accounts.SameKeycards))
// }
// Add keycards on sender
keycard1 := accounts.GetProfileKeycardForTest()
// func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfKeycardsWereDeletedOnSenderSide() {
// senderDb := s.main.settings
// dbOnReceiver := s.other.settings
keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// // Add keycards on sender
// keycard1 := accounts.GetProfileKeycardForTest()
keycard2Copy := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycard2Copy.KeycardUID = keycard2Copy.KeycardUID + "C"
keycard2Copy.KeycardName = keycard2Copy.KeycardName + "Copy"
keycard2Copy.LastUpdateClock = keycard2Copy.LastUpdateClock + 1
// keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycard3 := accounts.GetKeycardForSeedImportedKeypair2ForTest()
// keycard2Copy := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// keycard2Copy.KeycardUID = keycard2Copy.KeycardUID + "C"
// keycard2Copy.KeycardName = keycard2Copy.KeycardName + "Copy"
// keycard2Copy.LastUpdateClock = keycard2Copy.LastUpdateClock + 1
// Add keycards on sender
addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
// keycard3 := accounts.GetKeycardForSeedImportedKeypair2ForTest()
// Add keycards on receiver
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2Copy)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard3)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
// // Add keycards on sender
// addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
// 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)
// // Add keycards on receiver
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2Copy)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard3)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// Wait for the response
_, err = WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
return len(r.Keypairs) == 3 && len(r.Keycards) == 2
},
"expected to receive keycards",
)
s.Require().NoError(err)
// // Trigger's a sync between devices
// err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
// s.Require().NoError(err)
syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
s.Require().NoError(err)
s.Require().Equal(2, len(syncedKeycards))
s.Require().True(contains(syncedKeycards, keycard1, accounts.SameKeycards))
s.Require().True(contains(syncedKeycards, keycard2, accounts.SameKeycards))
}
// // Wait for the response
// _, err = WaitOnMessengerResponse(
// s.other,
// func(r *MessengerResponse) bool {
// success := len(r.Keypairs) == 3
// for _, kp := range r.Keypairs {
// if kp.KeyUID == keycard1.KeyUID {
// success = success && len(kp.Keycards) == 1
// } else if kp.KeyUID == keycard2.KeyUID {
// success = success && len(kp.Keycards) == 1
// }
// }
// return success
// },
// "expected to receive keycards",
// )
// s.Require().NoError(err)
func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverAndSenderHasNoKeycardsInCommon() {
senderDb := s.main.settings
dbOnReceiver := s.other.settings
// syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
// s.Require().NoError(err)
// s.Require().Equal(2, len(syncedKeycards))
// s.Require().True(contains(syncedKeycards, keycard1, accounts.SameKeycards))
// s.Require().True(contains(syncedKeycards, keycard2, accounts.SameKeycards))
// }
// Add keycards on sender
keycard1 := accounts.GetProfileKeycardForTest()
// func (s *MessengerSyncKeycardsStateSuite) TestSyncKeycardsIfReceiverAndSenderHasNoKeycardsInCommon() {
// senderDb := s.main.settings
// dbOnReceiver := s.other.settings
keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// // Add keycards on sender
// keycard1 := accounts.GetProfileKeycardForTest()
keycard2Copy := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycard2Copy.KeycardUID = keycard2Copy.KeycardUID + "C"
keycard2Copy.KeycardName = keycard2Copy.KeycardName + "Copy"
keycard2Copy.LastUpdateClock = keycard2Copy.LastUpdateClock + 1
// keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest()
keycard3 := accounts.GetKeycardForSeedImportedKeypair2ForTest()
// keycard2Copy := accounts.GetKeycardForSeedImportedKeypair1ForTest()
// keycard2Copy.KeycardUID = keycard2Copy.KeycardUID + "C"
// keycard2Copy.KeycardName = keycard2Copy.KeycardName + "Copy"
// keycard2Copy.LastUpdateClock = keycard2Copy.LastUpdateClock + 1
// Add keycards on sender
addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2Copy)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
// keycard3 := accounts.GetKeycardForSeedImportedKeypair2ForTest()
// Add keycards on receiver
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard3)
s.Require().NoError(err)
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
// // Add keycards on sender
// addedKc, addedAccs, err := senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = senderDb.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard2Copy)
// 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)
// // Add keycards on receiver
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard1)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// addedKc, addedAccs, err = dbOnReceiver.AddKeycardOrAddAccountsIfKeycardIsAdded(*keycard3)
// s.Require().NoError(err)
// s.Require().Equal(true, addedKc)
// s.Require().Equal(false, addedAccs)
// Wait for the response
_, err = WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
return len(r.Keypairs) == 3 && len(r.Keycards) == 2
},
"expected to receive keycards",
)
s.Require().NoError(err)
// // Trigger's a sync between devices
// err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
// s.Require().NoError(err)
syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
s.Require().NoError(err)
s.Require().Equal(2, len(syncedKeycards))
s.Require().True(contains(syncedKeycards, keycard2, accounts.SameKeycards))
s.Require().True(contains(syncedKeycards, keycard2Copy, accounts.SameKeycards))
}
// // Wait for the response
// _, err = WaitOnMessengerResponse(
// s.other,
// func(r *MessengerResponse) bool {
// success := len(r.Keypairs) == 3
// for _, kp := range r.Keypairs {
// if kp.KeyUID == keycard2.KeyUID {
// success = success && len(kp.Keycards) == 2
// }
// }
// return success
// },
// "expected to receive keycards",
// )
// s.Require().NoError(err)
// syncedKeycards, err := dbOnReceiver.GetAllKnownKeycards()
// s.Require().NoError(err)
// s.Require().Equal(2, len(syncedKeycards))
// s.Require().True(contains(syncedKeycards, keycard2, accounts.SameKeycards))
// s.Require().True(contains(syncedKeycards, keycard2Copy, accounts.SameKeycards))
// }

View File

@ -7,7 +7,6 @@ import (
"github.com/golang/protobuf/proto"
"go.uber.org/zap"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/protobuf"
@ -199,20 +198,20 @@ func (m *Messenger) HandleSyncRawMessages(rawMessages []*protobuf.RawMessage) er
if err != nil {
return err
}
err = m.HandleSyncWalletAccount(state, message, accounts.SyncedFromLocalPairing)
err = m.HandleSyncWatchOnlyAccount(state, message)
if err != nil {
m.logger.Error("failed to HandleSyncWalletAccount when HandleSyncRawMessages", zap.Error(err))
m.logger.Error("failed to HandleSyncWatchOnlyAccount when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_FULL_KEYPAIR:
var message protobuf.SyncKeypairFull
case protobuf.ApplicationMetadataMessage_SYNC_KEYPAIR:
var message protobuf.SyncKeypair
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.HandleSyncKeypairFull(state, message)
err = m.HandleSyncKeypair(state, message)
if err != nil {
m.logger.Error("failed to HandleSyncKeypairFull when HandleSyncRawMessages", zap.Error(err))
m.logger.Error("failed to HandleSyncKeypair when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_SAVED_ADDRESS:

View File

@ -109,6 +109,11 @@ func (s *MessengerSyncWalletSuite) TestProfileKeypairNameChange() {
func (s *MessengerSyncWalletSuite) TestSyncWallets() {
profileKp := accounts.GetProfileKeypairForTest(true, true, true)
// set clocks for accounts
profileKp.Clock = uint64(len(profileKp.Accounts) - 1)
for i, acc := range profileKp.Accounts {
acc.Clock = uint64(i)
}
// Create a main account on alice
err := s.m.settings.SaveOrUpdateKeypair(profileKp)
@ -183,7 +188,7 @@ func (s *MessengerSyncWalletSuite) TestSyncWallets() {
// Store watch only accounts on alice's device
woAccounts := accounts.GetWatchOnlyAccountsForTest()
err = s.m.settings.SaveOrUpdateAccounts(woAccounts)
err = s.m.settings.SaveOrUpdateAccounts(woAccounts, false)
s.Require().NoError(err)
dbWoAccounts1, err := s.m.settings.GetWatchOnlyAccounts()
s.Require().NoError(err)
@ -205,16 +210,37 @@ func (s *MessengerSyncWalletSuite) TestSyncWallets() {
}
if len(response.Keypairs) != 3 || // 3 keypairs (profile, seed, priv key)
len(response.Accounts) != len(woAccounts) {
len(response.WatchOnlyAccounts) != len(woAccounts) {
return errors.New("no sync wallet account received")
}
return nil
})
s.Require().NoError(err)
dbProfileKp2, err = s.m.settings.GetKeypairByKeyUID(profileKp.KeyUID)
dbProfileKp2, err = alicesOtherDevice.settings.GetKeypairByKeyUID(profileKp.KeyUID)
s.Require().NoError(err)
s.Require().True(accounts.SameKeypairsWithDifferentSyncedFrom(profileKp, dbProfileKp2, true, "", accounts.AccountFullyOperable))
s.Require().True(profileKp.KeyUID == dbProfileKp2.KeyUID &&
profileKp.Name == dbProfileKp2.Name &&
profileKp.Type == dbProfileKp2.Type &&
profileKp.DerivedFrom == dbProfileKp2.DerivedFrom &&
profileKp.LastUsedDerivationIndex == dbProfileKp2.LastUsedDerivationIndex &&
profileKp.Clock == dbProfileKp2.Clock &&
len(profileKp.Accounts) == len(dbProfileKp2.Accounts))
// chat and default wallet account should be fully operable, other accounts partially operable
for i := range profileKp.Accounts {
match := false
expectedOperableValue := accounts.AccountPartiallyOperable
if profileKp.Accounts[i].Chat || profileKp.Accounts[i].Wallet {
expectedOperableValue = accounts.AccountFullyOperable
}
for j := range dbProfileKp2.Accounts {
if accounts.SameAccountsWithDifferentOperable(profileKp.Accounts[i], dbProfileKp2.Accounts[j], expectedOperableValue) {
match = true
break
}
}
s.Require().True(match)
}
dbSeedPhraseKp2, err := alicesOtherDevice.settings.GetKeypairByKeyUID(seedPhraseKp.KeyUID)
s.Require().NoError(err)
@ -252,7 +278,7 @@ func (s *MessengerSyncWalletSuite) TestSyncWallets() {
}
if len(response.Keypairs) != 1 {
return errors.New("no sync wallet account received")
return errors.New("no sync keypairs received")
}
return nil
})
@ -280,8 +306,8 @@ func (s *MessengerSyncWalletSuite) TestSyncWallets() {
return err
}
if len(response.Accounts) != len(accountsToUpdate) {
return errors.New("no sync wallet account received")
if len(response.Keypairs) != 2 {
return errors.New("no sync keypairs received")
}
return nil
})

View File

@ -9,6 +9,7 @@ import (
"go.uber.org/zap"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/protocol/common"
@ -83,17 +84,59 @@ func (m *Messenger) UpdateKeypairName(keyUID string, name string) error {
return ErrCannotChangeKeypairName
}
clock, _ := m.getLastClockWithRelatedChat()
err := m.settings.UpdateKeypairName(keyUID, name, clock)
err := m.settings.UpdateKeypairName(keyUID, name, clock, keyUID == m.account.KeyUID)
if err != nil {
return err
}
dbKeypair, err := m.settings.GetKeypairByKeyUID(m.account.KeyUID)
return m.resolveAndSyncKeypairOrJustWalletAccount(keyUID, types.Address{}, clock, m.dispatchMessage)
}
func (m *Messenger) UpdateAccountPosition(address types.Address, position int64) error {
acc, err := m.settings.GetAccountByAddress(address)
if err != nil {
return err
}
return m.syncKeypair(dbKeypair, false, m.dispatchMessage)
clock, _ := m.getLastClockWithRelatedChat()
acc.Clock = clock
err = m.settings.UpdateAccountPosition(address, position, clock)
if err != nil {
return err
}
return m.resolveAndSyncKeypairOrJustWalletAccount(acc.KeyUID, acc.Address, acc.Clock, m.dispatchMessage)
}
func (m *Messenger) resolveAndSetAccountPropsMaintainedByBackend(acc *accounts.Account) error {
// Account position is fully maintained by the backend, no need client to set it explicitly.
// To support DragAndDrop feature for accounts there is exposed `UpdateAccountPosition` which
// moves an account to the passed position.
//
// Account operability is fully maintained by the backend, for new accounts created on this device
// it is always set to fully operable, while for accounts received by syncing process or fetched from waku
// is set by logic placed in `resolveAccountOperability` function.
//
// TODO: making not or partially operable accounts fully operable will be added later, but for sure it will
// be handled by the backend only, no need client to set it explicitly.
dbAccount, err := m.settings.GetAccountByAddress(acc.Address)
if err != nil && err != accounts.ErrDbAccountNotFound {
return err
}
if dbAccount != nil {
acc.Position = dbAccount.Position
acc.Operable = dbAccount.Operable
} else {
pos, err := m.settings.GetPositionForNextNewAccount()
if err != nil {
return err
}
acc.Position = pos
acc.Operable = accounts.AccountFullyOperable
}
return nil
}
func (m *Messenger) SaveOrUpdateKeypair(keypair *accounts.Keypair) error {
@ -106,56 +149,121 @@ func (m *Messenger) SaveOrUpdateKeypair(keypair *accounts.Keypair) error {
for _, acc := range keypair.Accounts {
acc.Clock = clock
err := m.resolveAndSetAccountPropsMaintainedByBackend(acc)
if err != nil {
return err
}
}
err := m.settings.SaveOrUpdateKeypair(keypair)
if err != nil {
return err
}
return m.syncKeypair(keypair, false, m.dispatchMessage)
return m.resolveAndSyncKeypairOrJustWalletAccount(keypair.KeyUID, types.Address{}, keypair.Clock, m.dispatchMessage)
}
func (m *Messenger) SaveOrUpdateAccount(acc *accounts.Account) error {
clock, _ := m.getLastClockWithRelatedChat()
acc.Clock = clock
err := m.settings.SaveOrUpdateAccounts([]*accounts.Account{acc})
err := m.resolveAndSetAccountPropsMaintainedByBackend(acc)
if err != nil {
return err
}
return m.syncWalletAccount(acc, m.dispatchMessage)
err = m.settings.SaveOrUpdateAccounts([]*accounts.Account{acc}, true)
if err != nil {
return err
}
return m.resolveAndSyncKeypairOrJustWalletAccount(acc.KeyUID, acc.Address, acc.Clock, m.dispatchMessage)
}
func (m *Messenger) deleteKeystoreFileForAddress(address types.Address) error {
acc, err := m.settings.GetAccountByAddress(address)
if err != nil {
return err
}
if acc.Operable == accounts.AccountNonOperable || acc.Operable == accounts.AccountPartiallyOperable {
return nil
}
if acc.Type != accounts.AccountTypeWatch {
kp, err := m.settings.GetKeypairByKeyUID(acc.KeyUID)
if err != nil {
return err
}
lastAcccountOfKeypairWithTheSameKey := len(kp.Accounts) == 1
knownKeycardsForKeyUID, err := m.settings.GetKeycardByKeyUID(acc.KeyUID)
if err != nil {
return err
}
if len(knownKeycardsForKeyUID) == 0 {
err = m.accountsManager.DeleteAccount(address)
var e *account.ErrCannotLocateKeyFile
if err != nil && !errors.As(err, &e) {
return err
}
if acc.Type != accounts.AccountTypeKey {
if lastAcccountOfKeypairWithTheSameKey {
err = m.accountsManager.DeleteAccount(types.Address(ethcommon.HexToAddress(kp.DerivedFrom)))
var e *account.ErrCannotLocateKeyFile
if err != nil && !errors.As(err, &e) {
return err
}
}
}
} else {
if lastAcccountOfKeypairWithTheSameKey {
knownKeycards, err := m.settings.GetAllKnownKeycards()
if err != nil {
return err
}
for _, kc := range knownKeycards {
if kc.KeyUID == acc.KeyUID {
clock := uint64(time.Now().Unix())
err = m.RemoveMigratedAccountsForKeycard(context.Background(), kc.KeycardUID, kc.AccountsAddresses, clock)
if err != nil {
return err
}
}
}
}
}
}
return nil
}
func (m *Messenger) DeleteAccount(address types.Address) error {
err := m.deleteKeystoreFileForAddress(address)
if err != nil {
return err
}
acc, err := m.settings.GetAccountByAddress(address)
if err != nil {
return err
}
err = m.settings.DeleteAccount(address)
clock, _ := m.getLastClockWithRelatedChat()
err = m.settings.DeleteAccount(address, clock)
if err != nil {
return err
}
clock, chat := m.getLastClockWithRelatedChat()
acc.Clock = clock
acc.Removed = true
err = m.syncWalletAccount(acc, m.dispatchMessage)
if err != nil {
return err
}
chat.LastClockValue = clock
return m.saveChat(chat)
return m.resolveAndSyncKeypairOrJustWalletAccount(acc.KeyUID, acc.Address, clock, m.dispatchMessage)
}
func (m *Messenger) prepareSyncAccountMessage(acc *accounts.Account) *protobuf.SyncAccount {
if acc.Chat {
return nil
}
return &protobuf.SyncAccount{
Clock: acc.Clock,
Address: acc.Address.Bytes(),
@ -169,6 +277,7 @@ func (m *Messenger) prepareSyncAccountMessage(acc *accounts.Account) *protobuf.S
Chat: acc.Chat,
Hidden: acc.Hidden,
Removed: acc.Removed,
Position: acc.Position,
}
}
@ -194,6 +303,7 @@ func (m *Messenger) prepareSyncKeypairMessage(kp *accounts.Keypair) (*protobuf.S
DerivedFrom: kp.DerivedFrom,
LastUsedDerivationIndex: kp.LastUsedDerivationIndex,
SyncedFrom: kp.SyncedFrom,
Removed: kp.Removed,
}
if kp.SyncedFrom == "" {
@ -213,24 +323,13 @@ func (m *Messenger) prepareSyncKeypairMessage(kp *accounts.Keypair) (*protobuf.S
message.Accounts = append(message.Accounts, sAcc)
}
return message, nil
}
func (m *Messenger) prepareSyncKeypairFullMessage(kp *accounts.Keypair) (*protobuf.SyncKeypairFull, error) {
syncKpMsg, err := m.prepareSyncKeypairMessage(kp)
if err != nil {
return nil, err
}
syncKcMsgs, err := m.prepareSyncKeycardsMessage(kp.KeyUID)
if err != nil {
return nil, err
}
message.Keycards = syncKcMsgs
return &protobuf.SyncKeypairFull{
Keypair: syncKpMsg,
Keycards: syncKcMsgs,
}, nil
return message, nil
}
func (m *Messenger) syncWalletAccount(acc *accounts.Account, rawMessageHandler RawMessageHandler) error {
@ -261,7 +360,7 @@ func (m *Messenger) syncWalletAccount(acc *accounts.Account, rawMessageHandler R
return err
}
func (m *Messenger) syncKeypair(keypair *accounts.Keypair, fullKeypairSync bool, rawMessageHandler RawMessageHandler) (err error) {
func (m *Messenger) syncKeypair(keypair *accounts.Keypair, rawMessageHandler RawMessageHandler) (err error) {
if !m.hasPairedDevices() {
return nil
}
@ -273,32 +372,70 @@ func (m *Messenger) syncKeypair(keypair *accounts.Keypair, fullKeypairSync bool,
rawMessage := common.RawMessage{
LocalChatID: chat.ID,
ResendAutomatically: true,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_KEYPAIR,
}
if fullKeypairSync {
message, err := m.prepareSyncKeypairFullMessage(keypair)
if err != nil {
return err
}
message, err := m.prepareSyncKeypairMessage(keypair)
if err != nil {
return err
}
rawMessage.MessageType = protobuf.ApplicationMetadataMessage_SYNC_FULL_KEYPAIR
rawMessage.Payload, err = proto.Marshal(message)
if err != nil {
return err
}
} else {
message, err := m.prepareSyncKeypairMessage(keypair)
if err != nil {
return err
}
rawMessage.MessageType = protobuf.ApplicationMetadataMessage_SYNC_KEYPAIR
rawMessage.Payload, err = proto.Marshal(message)
if err != nil {
return err
}
rawMessage.Payload, err = proto.Marshal(message)
if err != nil {
return err
}
_, err = rawMessageHandler(ctx, rawMessage)
return err
}
// This function resolves which protobuf message needs to be sent.
//
// If `KeyUID` is empty (means it's a watch only account) we send `protobuf.SyncAccount` message
// otherwise means the account belong to a keypai, hence we send `protobuf.SyncKeypair` message
func (m *Messenger) resolveAndSyncKeypairOrJustWalletAccount(keyUID string, address types.Address, clock uint64, rawMessageHandler RawMessageHandler) error {
if !m.hasPairedDevices() {
return nil
}
if keyUID == "" {
dbAccount, err := m.settings.GetAccountByAddress(address)
if err != nil && err != accounts.ErrDbAccountNotFound {
return err
}
if dbAccount == nil {
dbAccount = &accounts.Account{
Address: address,
Clock: clock,
Removed: true,
}
}
err = m.syncWalletAccount(dbAccount, rawMessageHandler)
if err != nil {
return err
}
} else {
dbKeypair, err := m.settings.GetKeypairByKeyUID(keyUID)
if err != nil && err != accounts.ErrDbKeypairNotFound {
return err
}
if dbKeypair == nil {
// In case entire keypair was removed (happens if user removes the last account for a keypair).
dbKeypair = &accounts.Keypair{
KeyUID: keyUID,
Clock: clock,
Removed: true,
}
}
err = m.syncKeypair(dbKeypair, rawMessageHandler)
if err != nil {
return err
}
}
_, chat := m.getLastClockWithRelatedChat()
chat.LastClockValue = clock
return m.saveChat(chat)
}

View File

@ -88,10 +88,9 @@ const (
ApplicationMetadataMessage_SYNC_KEYCARD_ACTION ApplicationMetadataMessage_Type = 63
ApplicationMetadataMessage_SYNC_SOCIAL_LINKS ApplicationMetadataMessage_Type = 64
ApplicationMetadataMessage_SYNC_ENS_USERNAME_DETAIL ApplicationMetadataMessage_Type = 65
ApplicationMetadataMessage_SYNC_FULL_KEYPAIR ApplicationMetadataMessage_Type = 66
ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_NOTIFICATION ApplicationMetadataMessage_Type = 67
ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_NOTIFICATION_STATE ApplicationMetadataMessage_Type = 68
ApplicationMetadataMessage_COMMUNITY_ADMIN_MESSAGE ApplicationMetadataMessage_Type = 69
ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_NOTIFICATION ApplicationMetadataMessage_Type = 66
ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_NOTIFICATION_STATE ApplicationMetadataMessage_Type = 67
ApplicationMetadataMessage_COMMUNITY_ADMIN_MESSAGE ApplicationMetadataMessage_Type = 68
)
var ApplicationMetadataMessage_Type_name = map[int32]string{
@ -160,10 +159,9 @@ var ApplicationMetadataMessage_Type_name = map[int32]string{
63: "SYNC_KEYCARD_ACTION",
64: "SYNC_SOCIAL_LINKS",
65: "SYNC_ENS_USERNAME_DETAIL",
66: "SYNC_FULL_KEYPAIR",
67: "SYNC_ACTIVITY_CENTER_NOTIFICATION",
68: "SYNC_ACTIVITY_CENTER_NOTIFICATION_STATE",
69: "COMMUNITY_ADMIN_MESSAGE",
66: "SYNC_ACTIVITY_CENTER_NOTIFICATION",
67: "SYNC_ACTIVITY_CENTER_NOTIFICATION_STATE",
68: "COMMUNITY_ADMIN_MESSAGE",
}
var ApplicationMetadataMessage_Type_value = map[string]int32{
@ -232,10 +230,9 @@ var ApplicationMetadataMessage_Type_value = map[string]int32{
"SYNC_KEYCARD_ACTION": 63,
"SYNC_SOCIAL_LINKS": 64,
"SYNC_ENS_USERNAME_DETAIL": 65,
"SYNC_FULL_KEYPAIR": 66,
"SYNC_ACTIVITY_CENTER_NOTIFICATION": 67,
"SYNC_ACTIVITY_CENTER_NOTIFICATION_STATE": 68,
"COMMUNITY_ADMIN_MESSAGE": 69,
"SYNC_ACTIVITY_CENTER_NOTIFICATION": 66,
"SYNC_ACTIVITY_CENTER_NOTIFICATION_STATE": 67,
"COMMUNITY_ADMIN_MESSAGE": 68,
}
func (x ApplicationMetadataMessage_Type) String() string {
@ -314,67 +311,67 @@ func init() {
}
var fileDescriptor_ad09a6406fcf24c7 = []byte{
// 985 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x55, 0x6d, 0x73, 0x13, 0x37,
0x10, 0x6e, 0x20, 0x4d, 0x40, 0x79, 0x53, 0x44, 0x5e, 0x9c, 0x77, 0xc7, 0x40, 0x08, 0xd0, 0x9a,
0x16, 0xda, 0x4e, 0x5b, 0x4a, 0x5b, 0x59, 0xda, 0xd8, 0xc2, 0x77, 0xd2, 0x21, 0xe9, 0xdc, 0x71,
0xbf, 0x68, 0x4c, 0x71, 0x99, 0xcc, 0x00, 0xf1, 0x10, 0xe7, 0x43, 0x7e, 0x5a, 0x7f, 0x45, 0xff,
0x52, 0x47, 0xf7, 0xea, 0x24, 0x97, 0xe6, 0x53, 0xe2, 0xdd, 0x67, 0x77, 0xb5, 0xcf, 0x3e, 0xbb,
0x87, 0x1a, 0x83, 0xd1, 0xe8, 0xc3, 0xf1, 0x5f, 0x83, 0xf1, 0xf1, 0xc9, 0x27, 0xf7, 0x71, 0x38,
0x1e, 0xbc, 0x1b, 0x8c, 0x07, 0xee, 0xe3, 0xf0, 0xf4, 0x74, 0xf0, 0x7e, 0xd8, 0x1c, 0x7d, 0x3e,
0x19, 0x9f, 0x90, 0x3b, 0xc9, 0x9f, 0xb7, 0x67, 0x7f, 0x37, 0xfe, 0xc5, 0x68, 0x93, 0x96, 0x01,
// 977 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, 0xed, 0x18, 0x08, 0x01, 0x5a, 0xd3,
0x42, 0xdb, 0x69, 0x4b, 0x69, 0x2b, 0x4b, 0x37, 0xb6, 0xf0, 0xae, 0xb4, 0x48, 0x5a, 0x77, 0xdc,
0x2f, 0x1a, 0x53, 0x5c, 0x26, 0x33, 0x40, 0x3c, 0xc4, 0xf9, 0x90, 0xbf, 0xd3, 0x5f, 0xd1, 0x9f,
0xd7, 0xd1, 0x3e, 0x9d, 0x64, 0xd3, 0x7c, 0x4a, 0x7c, 0xef, 0xd1, 0x95, 0xee, 0xb9, 0xe7, 0x9e,
0x45, 0x8d, 0xc1, 0x68, 0xf4, 0xe1, 0xf8, 0xaf, 0xc1, 0xf8, 0xf8, 0xe4, 0x93, 0xfb, 0x38, 0x1c,
0x0f, 0xde, 0x0d, 0xc6, 0x03, 0xf7, 0x71, 0x78, 0x7a, 0x3a, 0x78, 0x3f, 0x6c, 0x8e, 0x3e, 0x9f,
0x8c, 0x4f, 0xc8, 0x9d, 0xe4, 0xcf, 0xdb, 0xb3, 0xbf, 0x1b, 0xff, 0x62, 0xb4, 0x49, 0xcb, 0x03,
0x61, 0x86, 0x0f, 0x53, 0x38, 0xd9, 0x46, 0x77, 0x4f, 0x8f, 0xdf, 0x7f, 0x1a, 0x8c, 0xcf, 0x3e,
0x0f, 0x6b, 0x53, 0xf5, 0xa9, 0xc3, 0x79, 0x5d, 0x1a, 0x48, 0x0d, 0xcd, 0x8e, 0x06, 0xe7, 0x1f,
0x4e, 0x06, 0xef, 0x6a, 0xb7, 0x12, 0x5f, 0xfe, 0x93, 0xbc, 0x42, 0xd3, 0xe3, 0xf3, 0xd1, 0xb0,
0x76, 0xbb, 0x3e, 0x75, 0xb8, 0xf8, 0xfc, 0x71, 0x33, 0xaf, 0xd7, 0xbc, 0xbe, 0x56, 0xd3, 0x9e,
0x8f, 0x86, 0x3a, 0x09, 0x6b, 0xfc, 0xb3, 0x84, 0xa6, 0xfd, 0x4f, 0x32, 0x87, 0x66, 0x63, 0xd9,
0x95, 0xea, 0x0f, 0x89, 0xbf, 0x20, 0x18, 0xcd, 0xb3, 0x0e, 0xb5, 0x2e, 0x04, 0x63, 0x68, 0x1b,
0xf0, 0x14, 0x21, 0x68, 0x91, 0x29, 0x69, 0x29, 0xb3, 0x2e, 0x8e, 0x38, 0xb5, 0x80, 0x6f, 0x91,
0x1d, 0xb4, 0x11, 0x42, 0xd8, 0x02, 0x6d, 0x3a, 0x22, 0xca, 0xcc, 0x45, 0xc8, 0x6d, 0xb2, 0x8a,
0x96, 0x23, 0x2a, 0xb4, 0x13, 0xd2, 0x58, 0x1a, 0x04, 0xd4, 0x0a, 0x25, 0xf1, 0xb4, 0x37, 0x9b,
0xbe, 0x64, 0x17, 0xcd, 0x5f, 0x92, 0xfb, 0x68, 0x4f, 0xc3, 0x9b, 0x18, 0x8c, 0x75, 0x94, 0x73,
0x0d, 0xc6, 0xb8, 0x23, 0xa5, 0x9d, 0xd5, 0x54, 0x1a, 0xca, 0x12, 0xd0, 0x0c, 0x79, 0x82, 0x0e,
0x28, 0x63, 0x10, 0x59, 0x77, 0x13, 0x76, 0x96, 0x3c, 0x45, 0x8f, 0x38, 0xb0, 0x40, 0x48, 0xb8,
0x11, 0x7c, 0x87, 0xac, 0xa3, 0x7b, 0x39, 0x68, 0xd2, 0x71, 0x97, 0xac, 0x20, 0x6c, 0x40, 0xf2,
0x0b, 0x56, 0x44, 0xf6, 0xd0, 0xd6, 0xe5, 0xdc, 0x93, 0x80, 0x39, 0x4f, 0xcd, 0x95, 0x26, 0x5d,
0x46, 0x20, 0x9e, 0xaf, 0x76, 0x53, 0xc6, 0x54, 0x2c, 0x2d, 0x5e, 0x20, 0xfb, 0x68, 0xe7, 0xaa,
0x3b, 0x8a, 0x5b, 0x81, 0x60, 0xce, 0xcf, 0x05, 0x2f, 0x92, 0x5d, 0xb4, 0x99, 0xcf, 0x83, 0x29,
0x0e, 0x8e, 0xf2, 0x1e, 0x68, 0x2b, 0x0c, 0x84, 0x20, 0x2d, 0x5e, 0x22, 0x0d, 0xb4, 0x1b, 0xc5,
0xa6, 0xe3, 0xa4, 0xb2, 0xe2, 0x48, 0xb0, 0x34, 0x85, 0x86, 0xb6, 0x30, 0x56, 0xa7, 0x94, 0x63,
0xcf, 0xd0, 0xff, 0x63, 0x9c, 0x06, 0x13, 0x29, 0x69, 0x00, 0x2f, 0x93, 0x2d, 0xb4, 0x7e, 0x15,
0xfc, 0x26, 0x06, 0xdd, 0xc7, 0x84, 0x3c, 0x40, 0xf5, 0x6b, 0x9c, 0x65, 0x8a, 0x7b, 0xbe, 0xeb,
0xaa, 0x7a, 0x09, 0x7f, 0x78, 0xc5, 0xb7, 0x54, 0xe5, 0xce, 0xc2, 0x57, 0xbd, 0x04, 0x21, 0x54,
0xaf, 0x85, 0xd3, 0x90, 0xf1, 0xbc, 0x46, 0x36, 0xd0, 0x6a, 0x5b, 0xab, 0x38, 0x4a, 0x68, 0x71,
0x42, 0xf6, 0x84, 0x4d, 0xbb, 0x5b, 0x27, 0xcb, 0x68, 0x21, 0x35, 0x72, 0x90, 0x56, 0xd8, 0x3e,
0xae, 0x79, 0x34, 0x53, 0x61, 0x18, 0x4b, 0x61, 0xfb, 0x8e, 0x83, 0x61, 0x5a, 0x44, 0x09, 0x7a,
0x83, 0xd4, 0xd0, 0x4a, 0xe9, 0x9a, 0xc8, 0xb3, 0xe9, 0x5f, 0x5d, 0x7a, 0x8a, 0x69, 0x2b, 0xf7,
0x5a, 0x09, 0x89, 0xb7, 0xc8, 0x12, 0x9a, 0x8b, 0x84, 0x2c, 0x64, 0xbf, 0xed, 0x77, 0x07, 0xb8,
0x28, 0x77, 0x67, 0xc7, 0xbf, 0xc4, 0x58, 0x6a, 0x63, 0x93, 0xaf, 0xce, 0xae, 0xef, 0x85, 0x43,
0x00, 0x13, 0xfb, 0xb2, 0xe7, 0x45, 0x55, 0xa5, 0x99, 0xac, 0x34, 0xae, 0x93, 0x4d, 0xb4, 0x46,
0xa5, 0x92, 0xfd, 0x50, 0xc5, 0xc6, 0x85, 0x60, 0xb5, 0x60, 0xae, 0x45, 0x2d, 0xeb, 0xe0, 0xfd,
0x62, 0xab, 0x92, 0x96, 0x35, 0x84, 0xaa, 0x07, 0x1c, 0x37, 0xfc, 0xd4, 0x4a, 0x73, 0x56, 0xca,
0x78, 0x02, 0x39, 0xbe, 0x4f, 0x10, 0x9a, 0x69, 0x51, 0xd6, 0x8d, 0x23, 0xfc, 0xa0, 0x50, 0xa4,
0x67, 0xb6, 0xe7, 0x3b, 0x65, 0x20, 0x2d, 0xe8, 0x14, 0xfa, 0xb0, 0x50, 0xe4, 0x65, 0x77, 0xba,
0x8d, 0xc0, 0xf1, 0x81, 0x57, 0x5c, 0x25, 0x84, 0x0b, 0x13, 0x0a, 0x63, 0x80, 0xe3, 0x47, 0x09,
0x13, 0x1e, 0xd3, 0x52, 0xaa, 0x1b, 0x52, 0xdd, 0xc5, 0x87, 0x64, 0x0d, 0x91, 0xf4, 0x85, 0x01,
0x50, 0xed, 0x3a, 0xc2, 0x58, 0xa5, 0xfb, 0xf8, 0xb1, 0xa7, 0x31, 0xb1, 0x1b, 0xb0, 0x56, 0xc8,
0x36, 0x7e, 0x42, 0xea, 0x68, 0xbb, 0x1c, 0x04, 0xd5, 0xac, 0x23, 0x7a, 0xe0, 0x42, 0xda, 0x96,
0x60, 0x03, 0x21, 0xbb, 0xf8, 0xa9, 0x1f, 0x62, 0x12, 0x13, 0x69, 0x75, 0x24, 0x02, 0x70, 0x91,
0x60, 0x36, 0xd6, 0x80, 0xbf, 0x2a, 0xb2, 0xe5, 0x3b, 0xf6, 0x75, 0x42, 0x66, 0x7a, 0x4a, 0xf2,
0x3d, 0xca, 0x95, 0xd8, 0xf4, 0xac, 0x69, 0xb0, 0x3a, 0x5d, 0xae, 0x8b, 0xce, 0x67, 0xe4, 0x00,
0x35, 0xae, 0xd5, 0x43, 0x29, 0xd7, 0x6f, 0x4a, 0xea, 0x0b, 0x70, 0xd6, 0x8a, 0xc1, 0xdf, 0xfa,
0x5e, 0xf2, 0xd0, 0xbc, 0x42, 0x0f, 0x74, 0x21, 0x7b, 0xfc, 0xdc, 0xab, 0xe1, 0xd2, 0xfb, 0x2e,
0x00, 0x5e, 0xf8, 0x14, 0xf9, 0x0d, 0xaa, 0x44, 0x7c, 0x57, 0x68, 0xc2, 0xea, 0xd8, 0x58, 0xe0,
0x2e, 0x36, 0xa0, 0xf1, 0xf7, 0xc5, 0xa8, 0x27, 0xd1, 0x45, 0x7f, 0x3f, 0x14, 0xa3, 0xbe, 0xd4,
0xb9, 0xe3, 0xc0, 0x84, 0xf1, 0x89, 0x7f, 0x4c, 0x8f, 0x4f, 0x05, 0x05, 0x01, 0xd0, 0x1e, 0xe0,
0x9f, 0xbc, 0x3f, 0x49, 0x91, 0x49, 0xdc, 0x9f, 0xdb, 0xb0, 0x54, 0xfa, 0xcf, 0xc5, 0xcc, 0x0d,
0xed, 0x01, 0xcf, 0xaf, 0x32, 0x7e, 0xe9, 0xcf, 0x48, 0x99, 0x97, 0x51, 0xc9, 0x20, 0xb8, 0xb2,
0x71, 0xbf, 0x78, 0x66, 0x32, 0x5f, 0x65, 0xdf, 0xaf, 0x8a, 0x61, 0x77, 0xa1, 0xef, 0x3f, 0x40,
0xf8, 0x57, 0x7f, 0xde, 0x73, 0x0b, 0xa3, 0x9a, 0xbb, 0xec, 0x7e, 0xfc, 0x56, 0x50, 0x64, 0x14,
0x13, 0x34, 0x70, 0x5e, 0x47, 0x06, 0xff, 0x4e, 0xb6, 0x51, 0x2d, 0x31, 0x83, 0x34, 0x09, 0x6b,
0x92, 0x86, 0xe0, 0x38, 0x58, 0x2a, 0x02, 0x4c, 0x8b, 0xa0, 0xa3, 0x38, 0x08, 0x8a, 0x22, 0x2d,
0xf2, 0x10, 0xed, 0x57, 0x2e, 0xc0, 0xe4, 0x3d, 0xc3, 0xcc, 0x5f, 0xdd, 0x1b, 0x61, 0xce, 0xdf,
0x0b, 0xc0, 0xdc, 0x8b, 0x68, 0x42, 0xf3, 0x3c, 0x9c, 0xb8, 0x34, 0xd0, 0x5a, 0xf8, 0x73, 0xae,
0xf9, 0xec, 0x65, 0xfe, 0xc1, 0x7f, 0x3b, 0x93, 0xfc, 0xf7, 0xe2, 0xbf, 0x00, 0x00, 0x00, 0xff,
0xff, 0xe0, 0x3a, 0xd3, 0x3a, 0x97, 0x08, 0x00, 0x00,
0x0f, 0x6b, 0x53, 0xf5, 0xa9, 0xc3, 0x79, 0x5d, 0x06, 0x48, 0x0d, 0xcd, 0x8e, 0x06, 0xe7, 0x1f,
0x4e, 0x06, 0xef, 0x6a, 0xb7, 0x92, 0x5c, 0xfe, 0x93, 0xbc, 0x42, 0xd3, 0xe3, 0xf3, 0xd1, 0xb0,
0x76, 0xbb, 0x3e, 0x75, 0xb8, 0xf8, 0xfc, 0x71, 0x33, 0xbf, 0xaf, 0x79, 0xfd, 0x5d, 0x4d, 0x7b,
0x3e, 0x1a, 0xea, 0xe4, 0x58, 0xe3, 0x9f, 0x25, 0x34, 0xed, 0x7f, 0x92, 0x39, 0x34, 0x1b, 0xcb,
0xae, 0x54, 0x7f, 0x48, 0xfc, 0x05, 0xc1, 0x68, 0x9e, 0x75, 0xa8, 0x75, 0x21, 0x18, 0x43, 0xdb,
0x80, 0xa7, 0x08, 0x41, 0x8b, 0x4c, 0x49, 0x4b, 0x99, 0x75, 0x71, 0xc4, 0xa9, 0x05, 0x7c, 0x8b,
0xec, 0xa0, 0x8d, 0x10, 0xc2, 0x16, 0x68, 0xd3, 0x11, 0x51, 0x16, 0x2e, 0x8e, 0xdc, 0x26, 0xab,
0x68, 0x39, 0xa2, 0x42, 0x3b, 0x21, 0x8d, 0xa5, 0x41, 0x40, 0xad, 0x50, 0x12, 0x4f, 0xfb, 0xb0,
0xe9, 0x4b, 0x76, 0x31, 0xfc, 0x25, 0xb9, 0x8f, 0xf6, 0x34, 0xbc, 0x89, 0xc1, 0x58, 0x47, 0x39,
0xd7, 0x60, 0x8c, 0x3b, 0x52, 0xda, 0x59, 0x4d, 0xa5, 0xa1, 0x2c, 0x01, 0xcd, 0x90, 0x27, 0xe8,
0x80, 0x32, 0x06, 0x91, 0x75, 0x37, 0x61, 0x67, 0xc9, 0x53, 0xf4, 0x88, 0x03, 0x0b, 0x84, 0x84,
0x1b, 0xc1, 0x77, 0xc8, 0x3a, 0xba, 0x97, 0x83, 0x26, 0x13, 0x77, 0xc9, 0x0a, 0xc2, 0x06, 0x24,
0xbf, 0x10, 0x45, 0x64, 0x0f, 0x6d, 0x5d, 0xae, 0x3d, 0x09, 0x98, 0xf3, 0xd4, 0x5c, 0x69, 0xd2,
0x65, 0x04, 0xe2, 0xf9, 0xea, 0x34, 0x65, 0x4c, 0xc5, 0xd2, 0xe2, 0x05, 0xb2, 0x8f, 0x76, 0xae,
0xa6, 0xa3, 0xb8, 0x15, 0x08, 0xe6, 0xfc, 0x5c, 0xf0, 0x22, 0xd9, 0x45, 0x9b, 0xf9, 0x3c, 0x98,
0xe2, 0xe0, 0x28, 0xef, 0x81, 0xb6, 0xc2, 0x40, 0x08, 0xd2, 0xe2, 0x25, 0xd2, 0x40, 0xbb, 0x51,
0x6c, 0x3a, 0x4e, 0x2a, 0x2b, 0x8e, 0x04, 0x4b, 0x4b, 0x68, 0x68, 0x0b, 0x63, 0x75, 0x4a, 0x39,
0xf6, 0x0c, 0xfd, 0x3f, 0xc6, 0x69, 0x30, 0x91, 0x92, 0x06, 0xf0, 0x32, 0xd9, 0x42, 0xeb, 0x57,
0xc1, 0x6f, 0x62, 0xd0, 0x7d, 0x4c, 0xc8, 0x03, 0x54, 0xbf, 0x26, 0x59, 0x96, 0xb8, 0xe7, 0xbb,
0xae, 0xba, 0x2f, 0xe1, 0x0f, 0xaf, 0xf8, 0x96, 0xaa, 0xd2, 0xd9, 0xf1, 0x55, 0x2f, 0x41, 0x08,
0xd5, 0x6b, 0xe1, 0x34, 0x64, 0x3c, 0xaf, 0x91, 0x0d, 0xb4, 0xda, 0xd6, 0x2a, 0x8e, 0x12, 0x5a,
0x9c, 0x90, 0x3d, 0x61, 0xd3, 0xee, 0xd6, 0xc9, 0x32, 0x5a, 0x48, 0x83, 0x1c, 0xa4, 0x15, 0xb6,
0x8f, 0x6b, 0x1e, 0xcd, 0x54, 0x18, 0xc6, 0x52, 0xd8, 0xbe, 0xe3, 0x60, 0x98, 0x16, 0x51, 0x82,
0xde, 0x20, 0x35, 0xb4, 0x52, 0xa6, 0x26, 0xea, 0x6c, 0xfa, 0x57, 0x97, 0x99, 0x62, 0xda, 0xca,
0xbd, 0x56, 0x42, 0xe2, 0x2d, 0xb2, 0x84, 0xe6, 0x22, 0x21, 0x0b, 0xd9, 0x6f, 0xfb, 0xdd, 0x01,
0x2e, 0xca, 0xdd, 0xd9, 0xf1, 0x2f, 0x31, 0x96, 0xda, 0xd8, 0xe4, 0xab, 0xb3, 0xeb, 0x7b, 0xe1,
0x10, 0xc0, 0xc4, 0xbe, 0xec, 0x79, 0x51, 0x55, 0x69, 0x26, 0xbb, 0x1a, 0xd7, 0xc9, 0x26, 0x5a,
0xa3, 0x52, 0xc9, 0x7e, 0xa8, 0x62, 0xe3, 0x42, 0xb0, 0x5a, 0x30, 0xd7, 0xa2, 0x96, 0x75, 0xf0,
0x7e, 0xb1, 0x55, 0x49, 0xcb, 0x1a, 0x42, 0xd5, 0x03, 0x8e, 0x1b, 0x7e, 0x6a, 0x65, 0x38, 0xbb,
0xca, 0x78, 0x02, 0x39, 0xbe, 0x4f, 0x10, 0x9a, 0x69, 0x51, 0xd6, 0x8d, 0x23, 0xfc, 0xa0, 0x50,
0xa4, 0x67, 0xb6, 0xe7, 0x3b, 0x65, 0x20, 0x2d, 0xe8, 0x14, 0xfa, 0xb0, 0x50, 0xe4, 0xe5, 0x74,
0xba, 0x8d, 0xc0, 0xf1, 0x81, 0x57, 0x5c, 0x25, 0x84, 0x0b, 0x13, 0x0a, 0x63, 0x80, 0xe3, 0x47,
0x09, 0x13, 0x1e, 0xd3, 0x52, 0xaa, 0x1b, 0x52, 0xdd, 0xc5, 0x87, 0x64, 0x0d, 0x91, 0xf4, 0x85,
0x01, 0x50, 0xed, 0x3a, 0xc2, 0x58, 0xa5, 0xfb, 0xf8, 0xb1, 0xa7, 0x31, 0x89, 0x1b, 0xb0, 0x56,
0xc8, 0x36, 0x7e, 0x42, 0xea, 0x68, 0xbb, 0x1c, 0x04, 0xd5, 0xac, 0x23, 0x7a, 0xe0, 0x42, 0xda,
0x96, 0x60, 0x03, 0x21, 0xbb, 0xf8, 0xa9, 0x1f, 0x62, 0x72, 0x26, 0xd2, 0xea, 0x48, 0x04, 0xe0,
0x22, 0xc1, 0x6c, 0xac, 0x01, 0x7f, 0x55, 0x54, 0xcb, 0x77, 0xec, 0xeb, 0x84, 0xcc, 0xd4, 0x4a,
0xf2, 0x3d, 0xca, 0x95, 0xd8, 0xf4, 0xac, 0x69, 0xb0, 0x3a, 0x5d, 0xae, 0x8b, 0xc9, 0x67, 0xe4,
0x00, 0x35, 0xae, 0xd5, 0x43, 0x29, 0xd7, 0x6f, 0x4a, 0xea, 0x0b, 0x70, 0xd6, 0x8a, 0xc1, 0xdf,
0xfa, 0x5e, 0xf2, 0xa3, 0xf9, 0x0d, 0x3d, 0xd0, 0x85, 0xec, 0xf1, 0x73, 0xaf, 0x86, 0x4b, 0xef,
0xbb, 0x00, 0x78, 0xe1, 0x4b, 0xe4, 0x1e, 0x54, 0x89, 0xf8, 0xae, 0xd0, 0x84, 0xd5, 0xb1, 0xb1,
0xc0, 0x5d, 0x6c, 0x40, 0xe3, 0xef, 0x8b, 0x51, 0x4f, 0xa2, 0x8b, 0xfe, 0x7e, 0x28, 0x46, 0x7d,
0xa9, 0x73, 0xc7, 0x81, 0x09, 0xe3, 0x0b, 0xff, 0x98, 0x9a, 0x4f, 0x05, 0x05, 0x01, 0xd0, 0x1e,
0xe0, 0x9f, 0x7c, 0x3e, 0x29, 0x91, 0x49, 0xdc, 0xdb, 0x6d, 0x58, 0x2a, 0xfd, 0xe7, 0x62, 0xe6,
0x86, 0xf6, 0x80, 0xe7, 0xae, 0x8c, 0x5f, 0x7a, 0x1b, 0x29, 0xeb, 0x32, 0x2a, 0x19, 0x04, 0x57,
0x36, 0xee, 0x17, 0xcf, 0x4c, 0x96, 0xab, 0xec, 0xfb, 0x55, 0x31, 0xec, 0x2e, 0xf4, 0xfd, 0x07,
0x08, 0xff, 0xea, 0xed, 0x3d, 0x8f, 0x30, 0xaa, 0xb9, 0xcb, 0xfc, 0xe3, 0xb7, 0x82, 0x22, 0xa3,
0x98, 0xa0, 0x81, 0xf3, 0x3a, 0x32, 0xf8, 0x77, 0xb2, 0x8d, 0x6a, 0x49, 0x18, 0xa4, 0x49, 0x58,
0x93, 0x34, 0x04, 0xc7, 0xc1, 0x52, 0x11, 0x60, 0x4a, 0x1e, 0xa2, 0xfd, 0x4a, 0xa5, 0x4f, 0x1a,
0x17, 0x6e, 0x79, 0x7b, 0xbd, 0x11, 0xe6, 0xbc, 0x31, 0x00, 0x66, 0x5e, 0x2d, 0x13, 0xe2, 0xe6,
0xe1, 0x84, 0xa5, 0xf0, 0xd6, 0xc2, 0x9f, 0x73, 0xcd, 0x67, 0x2f, 0xf3, 0x2f, 0xfb, 0xdb, 0x99,
0xe4, 0xbf, 0x17, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xf3, 0x96, 0x25, 0x5f, 0x80, 0x08, 0x00,
0x00,
}

View File

@ -78,9 +78,8 @@ message ApplicationMetadataMessage {
SYNC_KEYCARD_ACTION = 63;
SYNC_SOCIAL_LINKS = 64;
SYNC_ENS_USERNAME_DETAIL = 65;
SYNC_FULL_KEYPAIR = 66;
SYNC_ACTIVITY_CENTER_NOTIFICATION = 67;
SYNC_ACTIVITY_CENTER_NOTIFICATION_STATE = 68;
COMMUNITY_ADMIN_MESSAGE = 69;
SYNC_ACTIVITY_CENTER_NOTIFICATION = 66;
SYNC_ACTIVITY_CENTER_NOTIFICATION_STATE = 67;
COMMUNITY_ADMIN_MESSAGE = 68;
}
}

View File

@ -168,7 +168,7 @@ func (x SyncTrustedUser_TrustStatus) String() string {
}
func (SyncTrustedUser_TrustStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{31, 0}
return fileDescriptor_d61ab7221f0b5518, []int{30, 0}
}
type SyncVerificationRequest_VerificationStatus int32
@ -202,7 +202,7 @@ func (x SyncVerificationRequest_VerificationStatus) String() string {
}
func (SyncVerificationRequest_VerificationStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{32, 0}
return fileDescriptor_d61ab7221f0b5518, []int{31, 0}
}
type SyncContactRequestDecision_DecisionStatus int32
@ -227,7 +227,7 @@ func (x SyncContactRequestDecision_DecisionStatus) String() string {
}
func (SyncContactRequestDecision_DecisionStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{33, 0}
return fileDescriptor_d61ab7221f0b5518, []int{32, 0}
}
type SyncKeycardAction_Action int32
@ -270,7 +270,7 @@ func (x SyncKeycardAction_Action) String() string {
}
func (SyncKeycardAction_Action) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{38, 0}
return fileDescriptor_d61ab7221f0b5518, []int{37, 0}
}
// `FetchingBackedUpDataDetails` is used to describe how many messages a single backup data structure consists of
@ -334,8 +334,8 @@ type Backup struct {
ProfileDetails *FetchingBackedUpDataDetails `protobuf:"bytes,8,opt,name=profileDetails,proto3" json:"profileDetails,omitempty"`
Setting *SyncSetting `protobuf:"bytes,9,opt,name=setting,proto3" json:"setting,omitempty"`
SettingsDetails *FetchingBackedUpDataDetails `protobuf:"bytes,10,opt,name=settingsDetails,proto3" json:"settingsDetails,omitempty"`
FullKeypair *SyncKeypairFull `protobuf:"bytes,11,opt,name=fullKeypair,proto3" json:"fullKeypair,omitempty"`
FullKeypairDetails *FetchingBackedUpDataDetails `protobuf:"bytes,12,opt,name=fullKeypairDetails,proto3" json:"fullKeypairDetails,omitempty"`
Keypair *SyncKeypair `protobuf:"bytes,11,opt,name=keypair,proto3" json:"keypair,omitempty"`
KeypairDetails *FetchingBackedUpDataDetails `protobuf:"bytes,12,opt,name=keypairDetails,proto3" json:"keypairDetails,omitempty"`
WatchOnlyAccount *SyncAccount `protobuf:"bytes,13,opt,name=watchOnlyAccount,proto3" json:"watchOnlyAccount,omitempty"`
WatchOnlyAccountDetails *FetchingBackedUpDataDetails `protobuf:"bytes,14,opt,name=watchOnlyAccountDetails,proto3" json:"watchOnlyAccountDetails,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -438,16 +438,16 @@ func (m *Backup) GetSettingsDetails() *FetchingBackedUpDataDetails {
return nil
}
func (m *Backup) GetFullKeypair() *SyncKeypairFull {
func (m *Backup) GetKeypair() *SyncKeypair {
if m != nil {
return m.FullKeypair
return m.Keypair
}
return nil
}
func (m *Backup) GetFullKeypairDetails() *FetchingBackedUpDataDetails {
func (m *Backup) GetKeypairDetails() *FetchingBackedUpDataDetails {
if m != nil {
return m.FullKeypairDetails
return m.KeypairDetails
}
return nil
}
@ -2429,6 +2429,7 @@ type SyncAccount struct {
Chat bool `protobuf:"varint,10,opt,name=chat,proto3" json:"chat,omitempty"`
Hidden bool `protobuf:"varint,11,opt,name=hidden,proto3" json:"hidden,omitempty"`
Removed bool `protobuf:"varint,12,opt,name=removed,proto3" json:"removed,omitempty"`
Position int64 `protobuf:"varint,13,opt,name=position,proto3" json:"position,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -2543,6 +2544,13 @@ func (m *SyncAccount) GetRemoved() bool {
return false
}
func (m *SyncAccount) GetPosition() int64 {
if m != nil {
return m.Position
}
return 0
}
type SyncKeypair struct {
Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"`
KeyUid string `protobuf:"bytes,2,opt,name=key_uid,json=keyUid,proto3" json:"key_uid,omitempty"`
@ -2552,6 +2560,8 @@ type SyncKeypair struct {
LastUsedDerivationIndex uint64 `protobuf:"varint,6,opt,name=last_used_derivation_index,json=lastUsedDerivationIndex,proto3" json:"last_used_derivation_index,omitempty"`
SyncedFrom string `protobuf:"bytes,7,opt,name=synced_from,json=syncedFrom,proto3" json:"synced_from,omitempty"`
Accounts []*SyncAccount `protobuf:"bytes,8,rep,name=accounts,proto3" json:"accounts,omitempty"`
Keycards []*SyncKeycard `protobuf:"bytes,9,rep,name=keycards,proto3" json:"keycards,omitempty"`
Removed bool `protobuf:"varint,10,opt,name=removed,proto3" json:"removed,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -2638,53 +2648,20 @@ func (m *SyncKeypair) GetAccounts() []*SyncAccount {
return nil
}
type SyncKeypairFull struct {
Keypair *SyncKeypair `protobuf:"bytes,1,opt,name=keypair,proto3" json:"keypair,omitempty"`
Keycards []*SyncKeycard `protobuf:"bytes,2,rep,name=keycards,proto3" json:"keycards,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SyncKeypairFull) Reset() { *m = SyncKeypairFull{} }
func (m *SyncKeypairFull) String() string { return proto.CompactTextString(m) }
func (*SyncKeypairFull) ProtoMessage() {}
func (*SyncKeypairFull) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{28}
}
func (m *SyncKeypairFull) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SyncKeypairFull.Unmarshal(m, b)
}
func (m *SyncKeypairFull) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SyncKeypairFull.Marshal(b, m, deterministic)
}
func (m *SyncKeypairFull) XXX_Merge(src proto.Message) {
xxx_messageInfo_SyncKeypairFull.Merge(m, src)
}
func (m *SyncKeypairFull) XXX_Size() int {
return xxx_messageInfo_SyncKeypairFull.Size(m)
}
func (m *SyncKeypairFull) XXX_DiscardUnknown() {
xxx_messageInfo_SyncKeypairFull.DiscardUnknown(m)
}
var xxx_messageInfo_SyncKeypairFull proto.InternalMessageInfo
func (m *SyncKeypairFull) GetKeypair() *SyncKeypair {
if m != nil {
return m.Keypair
}
return nil
}
func (m *SyncKeypairFull) GetKeycards() []*SyncKeycard {
func (m *SyncKeypair) GetKeycards() []*SyncKeycard {
if m != nil {
return m.Keycards
}
return nil
}
func (m *SyncKeypair) GetRemoved() bool {
if m != nil {
return m.Removed
}
return false
}
type SyncSavedAddress struct {
Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
@ -2703,7 +2680,7 @@ func (m *SyncSavedAddress) Reset() { *m = SyncSavedAddress{} }
func (m *SyncSavedAddress) String() string { return proto.CompactTextString(m) }
func (*SyncSavedAddress) ProtoMessage() {}
func (*SyncSavedAddress) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{29}
return fileDescriptor_d61ab7221f0b5518, []int{28}
}
func (m *SyncSavedAddress) XXX_Unmarshal(b []byte) error {
@ -2793,7 +2770,7 @@ func (m *SyncCommunitySettings) Reset() { *m = SyncCommunitySettings{} }
func (m *SyncCommunitySettings) String() string { return proto.CompactTextString(m) }
func (*SyncCommunitySettings) ProtoMessage() {}
func (*SyncCommunitySettings) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{30}
return fileDescriptor_d61ab7221f0b5518, []int{29}
}
func (m *SyncCommunitySettings) XXX_Unmarshal(b []byte) error {
@ -2848,7 +2825,7 @@ func (m *SyncTrustedUser) Reset() { *m = SyncTrustedUser{} }
func (m *SyncTrustedUser) String() string { return proto.CompactTextString(m) }
func (*SyncTrustedUser) ProtoMessage() {}
func (*SyncTrustedUser) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{31}
return fileDescriptor_d61ab7221f0b5518, []int{30}
}
func (m *SyncTrustedUser) XXX_Unmarshal(b []byte) error {
@ -2909,7 +2886,7 @@ func (m *SyncVerificationRequest) Reset() { *m = SyncVerificationRequest
func (m *SyncVerificationRequest) String() string { return proto.CompactTextString(m) }
func (*SyncVerificationRequest) ProtoMessage() {}
func (*SyncVerificationRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{32}
return fileDescriptor_d61ab7221f0b5518, []int{31}
}
func (m *SyncVerificationRequest) XXX_Unmarshal(b []byte) error {
@ -3006,7 +2983,7 @@ func (m *SyncContactRequestDecision) Reset() { *m = SyncContactRequestDe
func (m *SyncContactRequestDecision) String() string { return proto.CompactTextString(m) }
func (*SyncContactRequestDecision) ProtoMessage() {}
func (*SyncContactRequestDecision) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{33}
return fileDescriptor_d61ab7221f0b5518, []int{32}
}
func (m *SyncContactRequestDecision) XXX_Unmarshal(b []byte) error {
@ -3065,7 +3042,7 @@ func (m *BackedUpProfile) Reset() { *m = BackedUpProfile{} }
func (m *BackedUpProfile) String() string { return proto.CompactTextString(m) }
func (*BackedUpProfile) ProtoMessage() {}
func (*BackedUpProfile) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{34}
return fileDescriptor_d61ab7221f0b5518, []int{33}
}
func (m *BackedUpProfile) XXX_Unmarshal(b []byte) error {
@ -3140,7 +3117,7 @@ func (m *RawMessage) Reset() { *m = RawMessage{} }
func (m *RawMessage) String() string { return proto.CompactTextString(m) }
func (*RawMessage) ProtoMessage() {}
func (*RawMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{35}
return fileDescriptor_d61ab7221f0b5518, []int{34}
}
func (m *RawMessage) XXX_Unmarshal(b []byte) error {
@ -3189,7 +3166,7 @@ func (m *SyncRawMessage) Reset() { *m = SyncRawMessage{} }
func (m *SyncRawMessage) String() string { return proto.CompactTextString(m) }
func (*SyncRawMessage) ProtoMessage() {}
func (*SyncRawMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{36}
return fileDescriptor_d61ab7221f0b5518, []int{35}
}
func (m *SyncRawMessage) XXX_Unmarshal(b []byte) error {
@ -3247,7 +3224,7 @@ 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{37}
return fileDescriptor_d61ab7221f0b5518, []int{36}
}
func (m *SyncKeycard) XXX_Unmarshal(b []byte) error {
@ -3323,7 +3300,7 @@ 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{38}
return fileDescriptor_d61ab7221f0b5518, []int{37}
}
func (m *SyncKeycardAction) XXX_Unmarshal(b []byte) error {
@ -3377,7 +3354,7 @@ func (m *SyncSocialLinks) Reset() { *m = SyncSocialLinks{} }
func (m *SyncSocialLinks) String() string { return proto.CompactTextString(m) }
func (*SyncSocialLinks) ProtoMessage() {}
func (*SyncSocialLinks) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{39}
return fileDescriptor_d61ab7221f0b5518, []int{38}
}
func (m *SyncSocialLinks) XXX_Unmarshal(b []byte) error {
@ -3451,7 +3428,6 @@ func init() {
proto.RegisterType((*SyncProfilePictures)(nil), "protobuf.SyncProfilePictures")
proto.RegisterType((*SyncAccount)(nil), "protobuf.SyncAccount")
proto.RegisterType((*SyncKeypair)(nil), "protobuf.SyncKeypair")
proto.RegisterType((*SyncKeypairFull)(nil), "protobuf.SyncKeypairFull")
proto.RegisterType((*SyncSavedAddress)(nil), "protobuf.SyncSavedAddress")
proto.RegisterType((*SyncCommunitySettings)(nil), "protobuf.SyncCommunitySettings")
proto.RegisterType((*SyncTrustedUser)(nil), "protobuf.SyncTrustedUser")
@ -3470,226 +3446,225 @@ func init() {
}
var fileDescriptor_d61ab7221f0b5518 = []byte{
// 3528 bytes of a gzipped FileDescriptorProto
// 3517 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x3a, 0x4d, 0x73, 0x23, 0x49,
0x56, 0x53, 0x92, 0xac, 0x8f, 0x27, 0x59, 0x2e, 0x67, 0x7b, 0xa6, 0xd5, 0xee, 0x9e, 0xed, 0xee,
0x9a, 0x9d, 0xd8, 0x86, 0x18, 0x3c, 0xe0, 0x01, 0x96, 0x9d, 0x8f, 0x18, 0xd4, 0x92, 0x66, 0xda,
0xe3, 0xb6, 0x6c, 0xd2, 0xf6, 0x0c, 0x4b, 0x10, 0x51, 0xa4, 0xab, 0xd2, 0x56, 0xad, 0x4b, 0x55,
0xa2, 0x32, 0x65, 0xa3, 0x3d, 0x10, 0x40, 0x04, 0x67, 0x08, 0x2e, 0xbb, 0xc7, 0x39, 0x13, 0x9c,
0x88, 0xe0, 0x40, 0x04, 0x07, 0x4e, 0x04, 0xff, 0x01, 0x7e, 0x01, 0xc1, 0x85, 0x1b, 0x47, 0xe2,
0x65, 0x66, 0x95, 0xaa, 0xf4, 0x61, 0xec, 0xe0, 0xc4, 0xc9, 0xf5, 0x5e, 0xbe, 0x7c, 0xf9, 0x32,
0xdf, 0xf7, 0x93, 0x61, 0x73, 0xc2, 0x82, 0x24, 0x88, 0xae, 0xf6, 0x26, 0x49, 0x2c, 0x63, 0x52,
0x57, 0x7f, 0x2e, 0xa6, 0x97, 0xbb, 0x8f, 0xbc, 0x11, 0x93, 0x6e, 0xe0, 0xf3, 0x48, 0x06, 0x72,
0xa6, 0x97, 0x77, 0x1f, 0x89, 0x59, 0xe4, 0xb9, 0x82, 0x4b, 0x19, 0x44, 0x57, 0xc2, 0x20, 0x1d,
0x36, 0x99, 0x84, 0x81, 0xc7, 0x64, 0x10, 0x47, 0xee, 0x98, 0x4b, 0xe6, 0x33, 0xc9, 0xdc, 0x31,
0x17, 0x82, 0x5d, 0x71, 0x43, 0xb3, 0xed, 0xc5, 0xe3, 0xf1, 0x34, 0x0a, 0x64, 0xc0, 0xcd, 0x36,
0x87, 0xc1, 0xd3, 0xaf, 0xb8, 0xf4, 0x46, 0x41, 0x74, 0xf5, 0x9a, 0x79, 0xd7, 0xdc, 0x3f, 0x9f,
0xf4, 0x99, 0x64, 0x7d, 0x2e, 0x59, 0x10, 0x0a, 0xf2, 0x1c, 0x9a, 0x8a, 0x4f, 0x34, 0x1d, 0x5f,
0xf0, 0xa4, 0x63, 0xbd, 0xb0, 0x5e, 0x6d, 0x52, 0x40, 0xd4, 0x50, 0x61, 0xc8, 0x4b, 0x68, 0xc9,
0x58, 0xb2, 0x30, 0xa5, 0x28, 0x29, 0x8a, 0xa6, 0xc2, 0x69, 0x12, 0xe7, 0xaf, 0x6b, 0x50, 0x45,
0xde, 0xd3, 0x09, 0xd9, 0x81, 0x0d, 0x2f, 0x8c, 0xbd, 0x6b, 0xc5, 0xa8, 0x42, 0x35, 0x40, 0xda,
0x50, 0x0a, 0x7c, 0xb5, 0xb3, 0x41, 0x4b, 0x81, 0x4f, 0xbe, 0x84, 0xba, 0x17, 0x47, 0x92, 0x79,
0x52, 0x74, 0xca, 0x2f, 0xca, 0xaf, 0x9a, 0xfb, 0x1f, 0xec, 0xa5, 0x2f, 0xb2, 0x77, 0x3a, 0x8b,
0xbc, 0x83, 0x48, 0x48, 0x16, 0x86, 0xea, 0xae, 0x3d, 0x4d, 0xf9, 0xed, 0x3e, 0xcd, 0x36, 0x91,
0x9f, 0x40, 0x33, 0x77, 0xd3, 0x4e, 0x45, 0xf1, 0x78, 0x5c, 0xe4, 0xd1, 0x33, 0x04, 0x33, 0x9a,
0xa7, 0x25, 0xc7, 0xb0, 0x95, 0xb2, 0x31, 0x6f, 0xd0, 0xd9, 0x78, 0x61, 0xbd, 0x6a, 0xee, 0x7f,
0x38, 0xdf, 0x7e, 0xc7, 0x83, 0xd1, 0xc5, 0xdd, 0xe4, 0x1c, 0x48, 0x8e, 0x7f, 0xca, 0xb3, 0xfa,
0x10, 0x9e, 0x2b, 0x18, 0x90, 0x4f, 0xa0, 0x36, 0x49, 0xe2, 0xcb, 0x20, 0xe4, 0x9d, 0x9a, 0xe2,
0xf5, 0x64, 0xce, 0x2b, 0xe5, 0x71, 0xa2, 0x09, 0x68, 0x4a, 0x49, 0x8e, 0xa0, 0x6d, 0x3e, 0x53,
0x39, 0xea, 0x0f, 0x91, 0x63, 0x61, 0x33, 0xf9, 0x18, 0x6a, 0xc6, 0x08, 0x3b, 0x0d, 0xc5, 0xe7,
0xdd, 0xe2, 0x13, 0x9f, 0xea, 0x45, 0x9a, 0x52, 0xe1, 0xe3, 0xa6, 0x56, 0x9b, 0x0a, 0x00, 0x0f,
0x7a, 0xdc, 0x85, 0xdd, 0xe4, 0x33, 0x68, 0x5e, 0x4e, 0xc3, 0xf0, 0x90, 0xcf, 0xd0, 0x81, 0x3a,
0xcd, 0xc5, 0x97, 0x40, 0x29, 0xcc, 0xe2, 0x57, 0xd3, 0x30, 0xa4, 0x79, 0x6a, 0xd4, 0x4c, 0x0e,
0x4c, 0x05, 0x6a, 0x3d, 0x48, 0x33, 0xcb, 0x0c, 0x48, 0x17, 0xec, 0x5b, 0x26, 0xbd, 0xd1, 0x71,
0x14, 0xce, 0xba, 0x9e, 0x17, 0x4f, 0x23, 0xd9, 0xd9, 0x5c, 0xf5, 0x3c, 0x66, 0x91, 0x2e, 0x91,
0x13, 0x17, 0x1e, 0x2f, 0xe2, 0x52, 0xf1, 0xda, 0x0f, 0x11, 0x6f, 0x1d, 0x17, 0xe7, 0x3f, 0x2b,
0xd0, 0x3a, 0x9a, 0x86, 0x32, 0x48, 0x4f, 0x24, 0x50, 0x89, 0xd8, 0x98, 0x2b, 0xbf, 0x6c, 0x50,
0xf5, 0x4d, 0x9e, 0x41, 0x43, 0x06, 0x63, 0x2e, 0x24, 0x1b, 0x4f, 0x94, 0x77, 0x96, 0xe9, 0x1c,
0x81, 0xab, 0x3a, 0x2c, 0x79, 0x71, 0xd4, 0x29, 0xab, 0x6d, 0x73, 0x04, 0xf9, 0x12, 0xc0, 0x8b,
0xc3, 0x38, 0x71, 0x47, 0x4c, 0x8c, 0x8c, 0x03, 0xbe, 0x98, 0x0b, 0x9d, 0x3f, 0x7b, 0xaf, 0x87,
0x84, 0x6f, 0x98, 0x18, 0xd1, 0x86, 0x97, 0x7e, 0x92, 0x27, 0x18, 0x03, 0x90, 0x41, 0xe0, 0x2b,
0x07, 0x2c, 0xd3, 0x9a, 0x82, 0x0f, 0x7c, 0xf2, 0x23, 0xd8, 0xba, 0xe6, 0x33, 0x8f, 0x25, 0xbe,
0x6b, 0xc2, 0xa6, 0x72, 0xa7, 0x06, 0x6d, 0x1b, 0xf4, 0x89, 0xc6, 0x92, 0xc7, 0x50, 0xbb, 0xe6,
0x33, 0x77, 0x1a, 0xf8, 0xca, 0x47, 0x1a, 0xb4, 0x7a, 0xcd, 0x67, 0xe7, 0x81, 0x4f, 0x3e, 0x87,
0x6a, 0x30, 0x66, 0x57, 0x1c, 0xed, 0x1f, 0x25, 0xfb, 0xe1, 0x1a, 0xc9, 0x0e, 0x4c, 0xdc, 0x3d,
0x40, 0x62, 0x6a, 0xf6, 0x90, 0x8f, 0xe1, 0x91, 0x37, 0x15, 0x32, 0x1e, 0x07, 0x3f, 0xd7, 0xd1,
0x56, 0x09, 0xa6, 0x5c, 0xa0, 0x41, 0x49, 0x61, 0x49, 0x5d, 0x6d, 0xf7, 0x25, 0x34, 0xb2, 0x3b,
0x62, 0x08, 0x0c, 0x22, 0x9f, 0xff, 0x49, 0xc7, 0x7a, 0x51, 0x7e, 0x55, 0xa6, 0x1a, 0xd8, 0xfd,
0x37, 0x0b, 0x36, 0x0b, 0xa7, 0xe5, 0x85, 0xb7, 0x0a, 0xc2, 0xa7, 0xaa, 0x2a, 0xe5, 0x54, 0xd5,
0x81, 0xda, 0x84, 0xcd, 0xc2, 0x98, 0xf9, 0x4a, 0x15, 0x2d, 0x9a, 0x82, 0x78, 0xdc, 0x6d, 0xe0,
0x4b, 0xd4, 0x01, 0x3e, 0xa2, 0x06, 0xc8, 0x7b, 0x50, 0x1d, 0xf1, 0xe0, 0x6a, 0x24, 0xcd, 0xdb,
0x1a, 0x88, 0xec, 0x42, 0x1d, 0x1d, 0x5c, 0x04, 0x3f, 0xe7, 0xea, 0x4d, 0xcb, 0x34, 0x83, 0xc9,
0x07, 0xb0, 0x99, 0xa8, 0x2f, 0x57, 0xb2, 0xe4, 0x8a, 0x4b, 0xf5, 0xa6, 0x65, 0xda, 0xd2, 0xc8,
0x33, 0x85, 0x9b, 0x07, 0xf8, 0x7a, 0x2e, 0xc0, 0x3b, 0xbf, 0x28, 0xc1, 0xa3, 0xb7, 0xb1, 0xc7,
0x42, 0xa3, 0x99, 0x13, 0x23, 0xdc, 0x6f, 0x41, 0xe5, 0x9a, 0xcf, 0x84, 0x7a, 0x8a, 0xe6, 0xfe,
0xcb, 0xb9, 0x16, 0x56, 0x10, 0xef, 0x1d, 0xf2, 0x19, 0x55, 0xe4, 0xe4, 0x53, 0x68, 0x8d, 0x51,
0x4d, 0xcc, 0x78, 0x57, 0x49, 0xf9, 0xc4, 0x7b, 0xab, 0x95, 0x48, 0x0b, 0xb4, 0x78, 0xc3, 0x09,
0x13, 0xe2, 0x36, 0x4e, 0x7c, 0x63, 0xb5, 0x19, 0x8c, 0xaf, 0x88, 0xe9, 0xf6, 0x90, 0xcf, 0xd4,
0x6b, 0x35, 0x68, 0x0a, 0x92, 0x57, 0x99, 0xc9, 0x19, 0xa1, 0x74, 0x56, 0x68, 0xd0, 0x45, 0xf4,
0xee, 0xaf, 0x41, 0x19, 0x37, 0xac, 0xf2, 0x27, 0x02, 0x15, 0x4c, 0x9c, 0x4a, 0xdc, 0x16, 0x55,
0xdf, 0xce, 0x3f, 0x5a, 0xf0, 0x6e, 0xe1, 0xb2, 0x9c, 0x27, 0x6f, 0x78, 0x18, 0xc6, 0x68, 0xe5,
0xc6, 0xba, 0xdd, 0x1b, 0x9e, 0x88, 0x20, 0x8e, 0x14, 0xb3, 0x0d, 0xda, 0x36, 0xe8, 0x6f, 0x35,
0x16, 0x0d, 0x65, 0xc2, 0xb9, 0x72, 0x14, 0xcd, 0xb9, 0x8a, 0xe0, 0x81, 0xaf, 0x72, 0x37, 0xbf,
0x09, 0x3c, 0xee, 0x2a, 0x51, 0xf4, 0x6d, 0x41, 0xa3, 0x86, 0x28, 0xd0, 0x9c, 0x40, 0xce, 0x26,
0xdc, 0xdc, 0xd9, 0x10, 0x9c, 0xcd, 0x26, 0x2a, 0x02, 0x88, 0xe0, 0x2a, 0x62, 0x72, 0x9a, 0x70,
0x75, 0xe1, 0x16, 0x9d, 0x23, 0x9c, 0xef, 0x2d, 0xb0, 0x51, 0xec, 0x7c, 0x36, 0x5e, 0x93, 0xe1,
0x7f, 0x04, 0x5b, 0x41, 0x8e, 0xca, 0xcd, 0xd2, 0x7d, 0x3b, 0x8f, 0x2e, 0xc8, 0xac, 0x44, 0x2a,
0x2f, 0x89, 0x94, 0x3e, 0x6c, 0xa5, 0x68, 0xfd, 0xe9, 0x13, 0x6d, 0xa8, 0xf2, 0x23, 0x05, 0x9d,
0xff, 0xb0, 0xe0, 0xf1, 0x9a, 0x82, 0xe1, 0x9e, 0xb5, 0xc8, 0x07, 0xb0, 0x69, 0xb2, 0x9e, 0xab,
0xdc, 0xdf, 0x88, 0xd4, 0x32, 0x48, 0xed, 0xab, 0x4f, 0xa0, 0xce, 0x23, 0xe1, 0xe6, 0x04, 0xab,
0xf1, 0x48, 0xa8, 0x37, 0x7e, 0x09, 0xad, 0x90, 0x09, 0xe9, 0x4e, 0x27, 0x3e, 0x93, 0x5c, 0xc7,
0xb2, 0x0a, 0x6d, 0x22, 0xee, 0x5c, 0xa3, 0xf0, 0xce, 0x62, 0x26, 0x24, 0x1f, 0xbb, 0x92, 0x5d,
0x61, 0x69, 0x50, 0xc6, 0x3b, 0x6b, 0xd4, 0x19, 0xbb, 0x12, 0xe4, 0x43, 0x68, 0x87, 0x68, 0x23,
0x6e, 0x14, 0x78, 0xd7, 0xea, 0x10, 0x1d, 0xce, 0x36, 0x15, 0x76, 0x68, 0x90, 0xce, 0x9f, 0x57,
0xe1, 0xc9, 0xda, 0xea, 0x88, 0xfc, 0x3a, 0xec, 0xe4, 0x05, 0x71, 0xd5, 0xde, 0x70, 0x66, 0x6e,
0x4f, 0x72, 0x02, 0xbd, 0xd5, 0x2b, 0xff, 0x8f, 0x9f, 0x02, 0x75, 0xcb, 0x7c, 0x9f, 0xfb, 0x2a,
0x28, 0xd7, 0xa9, 0x06, 0xd0, 0x4e, 0x2e, 0x50, 0xc9, 0xdc, 0x57, 0x65, 0x47, 0x9d, 0xa6, 0x20,
0xd2, 0x8f, 0xa7, 0x28, 0x53, 0x53, 0xd3, 0x2b, 0x00, 0xe9, 0x13, 0x3e, 0x8e, 0x6f, 0xb8, 0xaf,
0xaa, 0x82, 0x3a, 0x4d, 0x41, 0xf2, 0x02, 0x5a, 0x23, 0x26, 0x5c, 0xc5, 0xd6, 0x9d, 0x0a, 0x95,
0xdf, 0xeb, 0x14, 0x46, 0x4c, 0x74, 0x11, 0x75, 0xae, 0x92, 0xc4, 0x0d, 0x4f, 0x82, 0xcb, 0xb4,
0x22, 0x17, 0x92, 0xc9, 0xa9, 0x4e, 0xdf, 0x65, 0x4a, 0xf2, 0x4b, 0xa7, 0x6a, 0x45, 0x15, 0xd2,
0xc9, 0x54, 0xc8, 0x94, 0x72, 0x4b, 0x51, 0x36, 0x15, 0xce, 0x90, 0x7c, 0x01, 0x4f, 0x4d, 0x75,
0xe9, 0x26, 0xfc, 0x8f, 0xa7, 0x5c, 0x48, 0xad, 0x45, 0xb5, 0x85, 0x77, 0x6c, 0xb5, 0xa3, 0x63,
0x48, 0xa8, 0xa6, 0x50, 0xca, 0xc4, 0xfd, 0x7c, 0xfd, 0x76, 0xed, 0x06, 0xdb, 0x6b, 0xb7, 0xf7,
0x94, 0x67, 0x7c, 0x09, 0xcf, 0x16, 0xb7, 0xe3, 0x73, 0x48, 0x6e, 0x8e, 0x27, 0x6a, 0xff, 0x93,
0xe2, 0x7e, 0xaa, 0x28, 0xf4, 0xf9, 0xeb, 0x19, 0x68, 0x01, 0x1e, 0xad, 0x67, 0xa0, 0x25, 0x78,
0x09, 0x2d, 0x3f, 0x10, 0x93, 0x90, 0xcd, 0xb4, 0x7d, 0xed, 0x28, 0xd5, 0x37, 0x0d, 0x0e, 0x6d,
0xcc, 0xb9, 0x5d, 0xf6, 0xf7, 0xb4, 0xc4, 0x59, 0xed, 0xef, 0x4b, 0x46, 0x5d, 0x5a, 0x61, 0xd4,
0x8b, 0x96, 0x5b, 0x5e, 0xb2, 0x5c, 0xe7, 0x35, 0xec, 0x2e, 0x1e, 0x7c, 0x32, 0xbd, 0x08, 0x03,
0xaf, 0x37, 0x62, 0xf7, 0x8c, 0x35, 0xce, 0x3f, 0x94, 0x61, 0xb3, 0xd0, 0x9a, 0xfc, 0xaf, 0xfb,
0x5a, 0xca, 0x31, 0x9f, 0x43, 0x73, 0x92, 0x04, 0x37, 0x4c, 0x72, 0xf7, 0x9a, 0xcf, 0x4c, 0x05,
0x00, 0x06, 0x85, 0xd9, 0xe8, 0x05, 0x46, 0x55, 0xe1, 0x25, 0xc1, 0x04, 0xe5, 0x52, 0x7e, 0xd9,
0xa2, 0x79, 0x14, 0x16, 0x04, 0x3f, 0x8b, 0x83, 0xc8, 0x78, 0x65, 0x9d, 0x1a, 0x08, 0xd3, 0xa5,
0xb6, 0x55, 0xee, 0xab, 0x82, 0xa0, 0x4e, 0x33, 0x78, 0xee, 0x34, 0xb5, 0xbc, 0xd3, 0x1c, 0x83,
0x6d, 0xb4, 0x2b, 0x5c, 0x19, 0xbb, 0xc8, 0xc7, 0x54, 0x59, 0x1f, 0xae, 0x6b, 0xc0, 0x0c, 0xf9,
0x59, 0xfc, 0x4d, 0x1c, 0x44, 0xb4, 0x9d, 0x14, 0x60, 0xf2, 0x19, 0xd4, 0xd3, 0xb2, 0xdf, 0xb4,
0x19, 0xcf, 0xd7, 0x30, 0x32, 0xfd, 0x86, 0xa0, 0xd9, 0x06, 0xcc, 0x60, 0x3c, 0xf2, 0x92, 0xd9,
0x44, 0x66, 0x4e, 0x3f, 0x47, 0xa8, 0xfc, 0x36, 0xe1, 0x9e, 0x64, 0x73, 0xd7, 0x9f, 0x23, 0x30,
0x69, 0x19, 0x52, 0x74, 0x60, 0x55, 0xa8, 0xb4, 0xd4, 0xcb, 0xb5, 0xe7, 0xe8, 0x43, 0x3e, 0x13,
0x58, 0xde, 0x3c, 0xbd, 0xe3, 0x46, 0x46, 0x5f, 0x56, 0xa6, 0xaf, 0xf7, 0x01, 0x26, 0xca, 0x36,
0x94, 0xba, 0xb4, 0xfe, 0x1b, 0x1a, 0x83, 0xda, 0xca, 0x94, 0x5e, 0xce, 0x2b, 0xfd, 0x8e, 0xc0,
0xfa, 0x58, 0xd7, 0x2d, 0x69, 0xa9, 0xdc, 0xa0, 0x55, 0x04, 0x0f, 0x7c, 0xb4, 0xdb, 0xb4, 0x75,
0x9c, 0xe1, 0x6a, 0x55, 0x2b, 0x3e, 0xc3, 0x1d, 0x28, 0x25, 0x6a, 0xf7, 0xad, 0xe9, 0xc3, 0x14,
0x40, 0xbe, 0x82, 0xed, 0x84, 0xdf, 0x70, 0x16, 0x72, 0xdf, 0x35, 0x95, 0x53, 0x5a, 0x2b, 0xe7,
0xba, 0x2b, 0x6a, 0x48, 0xb2, 0x46, 0x26, 0x29, 0x22, 0x84, 0xf3, 0x37, 0x25, 0xb0, 0x17, 0xdd,
0x82, 0x7c, 0x91, 0x6b, 0xef, 0x97, 0x2a, 0xbf, 0x35, 0x09, 0x2c, 0xd7, 0xdc, 0x7f, 0x0d, 0x2d,
0xf3, 0x7a, 0x78, 0x4b, 0xd1, 0x29, 0x2d, 0x96, 0xf0, 0xeb, 0xfd, 0x90, 0x36, 0x27, 0xd9, 0x37,
0x36, 0x8f, 0xb5, 0xb4, 0x82, 0x2c, 0x2b, 0xbb, 0xba, 0x43, 0x8c, 0xf4, 0x8a, 0xe9, 0x8e, 0xff,
0xc3, 0x88, 0xc1, 0xf9, 0x31, 0x6c, 0xa9, 0x55, 0x14, 0xc8, 0xe4, 0x93, 0xfb, 0xc5, 0x87, 0xcf,
0x61, 0x27, 0xdd, 0x78, 0xa4, 0xe7, 0x3a, 0x82, 0x72, 0x76, 0xdf, 0xdd, 0xbf, 0x0b, 0xef, 0xe9,
0xae, 0x53, 0x06, 0x37, 0x81, 0x9c, 0xf5, 0x78, 0x24, 0x79, 0x72, 0xc7, 0x7e, 0x1b, 0xca, 0x81,
0xaf, 0x9f, 0xb7, 0x45, 0xf1, 0xd3, 0xe9, 0xeb, 0x18, 0x57, 0xe4, 0xd0, 0xf5, 0x3c, 0xae, 0x9c,
0xe9, 0xbe, 0x5c, 0x06, 0xda, 0x59, 0x8a, 0x5c, 0xfa, 0x81, 0x18, 0x07, 0x42, 0x3c, 0x80, 0x8d,
0x0b, 0x1f, 0x2c, 0xb3, 0x19, 0xc6, 0xb2, 0x90, 0x57, 0x39, 0xfa, 0x5a, 0x5a, 0xf1, 0x30, 0x69,
0x78, 0x36, 0x0c, 0xa6, 0x2b, 0xd1, 0xab, 0x30, 0x91, 0x0b, 0xce, 0x23, 0xf5, 0x54, 0x75, 0x5a,
0x1b, 0x31, 0x71, 0xca, 0x79, 0xe4, 0xfc, 0x95, 0x05, 0xcf, 0xef, 0x3e, 0x41, 0x90, 0x10, 0xde,
0x67, 0x66, 0xd9, 0xf5, 0xd4, 0xba, 0x1b, 0xe5, 0x09, 0x8c, 0x7d, 0xbf, 0x5a, 0x6c, 0xfc, 0xd7,
0x71, 0xa4, 0x4f, 0xd9, 0xfa, 0xd3, 0x9c, 0x7f, 0x6a, 0xc0, 0x0f, 0xee, 0xde, 0xbf, 0x14, 0x6a,
0x96, 0x7a, 0xf8, 0x4a, 0xbe, 0x87, 0xbf, 0x84, 0xed, 0xbc, 0xb8, 0xf3, 0x9a, 0xbb, 0xbd, 0xff,
0x93, 0xfb, 0x8a, 0xbc, 0x97, 0x07, 0xb0, 0x44, 0xa7, 0x76, 0xb4, 0x80, 0xc9, 0x07, 0xa8, 0x4a,
0x21, 0x40, 0x11, 0xa8, 0x24, 0x9c, 0xa5, 0x49, 0x47, 0x7d, 0xa3, 0xc8, 0x7e, 0x6a, 0x0d, 0x26,
0xe7, 0xcc, 0x11, 0x98, 0x90, 0x98, 0xb1, 0x38, 0x93, 0x77, 0x32, 0x18, 0xeb, 0x35, 0x33, 0xef,
0x54, 0xed, 0x67, 0x8b, 0xa6, 0x20, 0xa6, 0x37, 0x36, 0x95, 0xa3, 0xac, 0x4b, 0x37, 0x90, 0xee,
0x69, 0x27, 0xe1, 0x2c, 0x9d, 0x93, 0xaa, 0x14, 0xd1, 0xc2, 0x9e, 0x76, 0x12, 0xce, 0x8c, 0x8f,
0x2d, 0x45, 0xd1, 0xa6, 0x2e, 0x3b, 0xf2, 0x51, 0xf4, 0x12, 0xb6, 0xc7, 0x7c, 0x7c, 0xc1, 0x13,
0x31, 0x0a, 0x26, 0x69, 0x05, 0xd7, 0x7a, 0xe0, 0x43, 0x1e, 0x65, 0x1c, 0x74, 0xbd, 0x47, 0xed,
0xf1, 0x02, 0x86, 0xfc, 0x85, 0x35, 0xaf, 0xe1, 0x56, 0x95, 0x97, 0x9b, 0xea, 0xc8, 0xd7, 0xf7,
0x3e, 0x32, 0x6d, 0x0f, 0x96, 0xca, 0xd1, 0xac, 0x0c, 0x5b, 0x5e, 0xc2, 0x67, 0xf6, 0x79, 0xc8,
0x51, 0x03, 0x6d, 0xed, 0x32, 0x06, 0x5c, 0x70, 0xb6, 0xad, 0x05, 0x67, 0x73, 0xfe, 0xcb, 0x02,
0x7b, 0xd1, 0x5a, 0x08, 0x40, 0x75, 0x18, 0xe3, 0x97, 0xfd, 0x0e, 0xd9, 0x82, 0xe6, 0x90, 0xdf,
0x1e, 0x47, 0xfc, 0x2c, 0x3e, 0x8e, 0xb8, 0x6d, 0x91, 0xc7, 0xf0, 0x68, 0xc8, 0x6f, 0x4f, 0x74,
0x25, 0xf3, 0x75, 0x12, 0x4f, 0x27, 0x18, 0xfc, 0xec, 0x12, 0x69, 0x42, 0xed, 0x88, 0x47, 0xc8,
0xc4, 0x2e, 0x93, 0x06, 0x6c, 0x50, 0x54, 0x98, 0x5d, 0x21, 0x04, 0xda, 0xbd, 0x42, 0xfd, 0x68,
0x6f, 0x20, 0x93, 0x2c, 0x12, 0x1f, 0x44, 0x37, 0x81, 0x54, 0x87, 0xdb, 0x55, 0xb2, 0x03, 0xf6,
0x62, 0xca, 0xb6, 0x6b, 0xe4, 0x07, 0xb0, 0x9b, 0x61, 0xe7, 0x2a, 0x49, 0xd7, 0xeb, 0xe4, 0x11,
0x6c, 0x65, 0xeb, 0x87, 0x01, 0xb6, 0x0f, 0x76, 0x43, 0x9f, 0xb1, 0xf4, 0x60, 0x36, 0x38, 0x7f,
0x69, 0x81, 0xbd, 0xa8, 0x58, 0xd2, 0x81, 0x9d, 0x45, 0xdc, 0x81, 0x1f, 0xe2, 0x0b, 0x3c, 0x85,
0xc7, 0x8b, 0x2b, 0x27, 0x3c, 0xf2, 0x83, 0xe8, 0xca, 0xb6, 0xc8, 0x33, 0xe8, 0x2c, 0x2e, 0xa6,
0xd1, 0xd7, 0x2e, 0xad, 0x5a, 0xed, 0x73, 0x2f, 0xc4, 0x32, 0xce, 0x2e, 0x3b, 0x7f, 0x66, 0xc1,
0x93, 0xb5, 0xda, 0xc6, 0xe7, 0x3c, 0x8f, 0xae, 0xa3, 0xf8, 0x36, 0xb2, 0xdf, 0x41, 0x60, 0x7e,
0x66, 0x0b, 0xea, 0xb9, 0x33, 0x5a, 0x50, 0x9f, 0xf3, 0x24, 0x9b, 0xd0, 0xe8, 0xb1, 0xc8, 0xe3,
0x61, 0xc8, 0x7d, 0xbb, 0x82, 0xfb, 0xce, 0xb0, 0x5b, 0xe1, 0xbe, 0xbd, 0x41, 0xb6, 0x61, 0xf3,
0x3c, 0x52, 0xe0, 0x77, 0x71, 0x22, 0x47, 0x33, 0xbb, 0xea, 0x7c, 0x6f, 0x41, 0x0b, 0xed, 0xf1,
0x75, 0x1c, 0x5f, 0x8f, 0x59, 0x72, 0xbd, 0x3e, 0xd4, 0x4f, 0x93, 0xd0, 0x24, 0x2e, 0xfc, 0xcc,
0x7a, 0xfe, 0x72, 0xae, 0xe7, 0x7f, 0x0a, 0x0d, 0x55, 0xaf, 0xbb, 0x48, 0xab, 0x83, 0x4a, 0x5d,
0x21, 0xce, 0x93, 0x30, 0xdf, 0xb8, 0x6d, 0x14, 0x1b, 0xb7, 0xf7, 0x01, 0x8c, 0xb1, 0xa2, 0x85,
0x56, 0xb5, 0x85, 0x1a, 0x4c, 0x57, 0x3a, 0x7f, 0x0a, 0xef, 0xa2, 0x84, 0x83, 0x48, 0x9c, 0x0b,
0x9e, 0xe0, 0x41, 0x7a, 0x62, 0xba, 0x46, 0xd4, 0x5d, 0xa8, 0x4f, 0x0d, 0x9d, 0x91, 0x37, 0x83,
0xd5, 0x00, 0x73, 0xc4, 0x02, 0x35, 0xeb, 0xd0, 0x85, 0x5c, 0x4d, 0xc1, 0x07, 0x85, 0xbe, 0xb2,
0x52, 0x10, 0xcf, 0xf9, 0x46, 0x97, 0x4b, 0xbd, 0x90, 0xb3, 0xe4, 0x4d, 0x20, 0x64, 0x9c, 0xcc,
0xf2, 0xc1, 0xd3, 0x2a, 0x04, 0xcf, 0xf7, 0x01, 0x3c, 0x24, 0xd4, 0x77, 0x31, 0xc1, 0xdd, 0x60,
0xba, 0xd2, 0xf9, 0x57, 0x0b, 0x08, 0x32, 0x33, 0xbf, 0x02, 0x9c, 0x04, 0x9e, 0x9c, 0x26, 0x7c,
0xe5, 0x64, 0x2a, 0x37, 0x3e, 0x2c, 0xad, 0x19, 0x1f, 0x96, 0xd5, 0x60, 0x65, 0x69, 0x7c, 0x58,
0x51, 0xe8, 0x74, 0x7c, 0xf8, 0x14, 0x1a, 0xaa, 0x93, 0x52, 0xf3, 0x43, 0x3d, 0x8a, 0x51, 0xf3,
0xc3, 0xd3, 0x95, 0xf3, 0xc3, 0xaa, 0x22, 0x58, 0x33, 0x3f, 0xac, 0xe5, 0xe7, 0x87, 0x23, 0x78,
0xb4, 0x7c, 0x13, 0xb1, 0x7e, 0x44, 0xfa, 0x3b, 0x50, 0x9f, 0x18, 0x22, 0x53, 0x1e, 0x3e, 0x2b,
0x86, 0xc4, 0x22, 0x27, 0x9a, 0x51, 0x3b, 0x7f, 0x57, 0x82, 0x66, 0x6e, 0x36, 0xbf, 0x46, 0xef,
0x1d, 0xa8, 0x31, 0xdf, 0x4f, 0xb8, 0x10, 0xe9, 0x7b, 0x19, 0x30, 0x2f, 0x52, 0xb9, 0x20, 0x52,
0xb1, 0xe6, 0xd7, 0x1d, 0x58, 0xae, 0xe6, 0x27, 0x50, 0x99, 0x30, 0x39, 0x32, 0xf5, 0xbb, 0xfa,
0xce, 0x34, 0x55, 0xcd, 0x69, 0x2a, 0x3f, 0x16, 0xaf, 0x99, 0x19, 0xa5, 0x19, 0x8b, 0xef, 0xc0,
0x06, 0x1f, 0xc7, 0x3f, 0x0b, 0x54, 0xee, 0x6b, 0x50, 0x0d, 0xa0, 0xaa, 0x6e, 0x59, 0x18, 0x72,
0x69, 0x46, 0x21, 0x06, 0x42, 0xe6, 0x68, 0x46, 0xa6, 0x27, 0x52, 0xdf, 0x4a, 0xad, 0x81, 0xef,
0xf3, 0xc8, 0xf4, 0x42, 0x06, 0x5a, 0x3f, 0x07, 0x71, 0x7e, 0x69, 0x9e, 0x2b, 0xfd, 0x49, 0x65,
0xf5, 0x73, 0xe5, 0x1e, 0xa5, 0xb4, 0x72, 0x94, 0x5d, 0x2e, 0x4e, 0x49, 0x73, 0xd3, 0x48, 0xf5,
0xad, 0x1a, 0x7f, 0x9e, 0x04, 0x37, 0xdc, 0x77, 0x2f, 0x93, 0x78, 0x6c, 0x5e, 0xa9, 0x69, 0x70,
0x5f, 0x25, 0xf1, 0x98, 0x7c, 0x06, 0xbb, 0xba, 0x45, 0x17, 0xdc, 0x77, 0xd5, 0x82, 0x99, 0x34,
0xaa, 0x59, 0xbb, 0x76, 0xf4, 0xc7, 0xaa, 0x61, 0x17, 0xdc, 0xef, 0x67, 0xeb, 0x07, 0xb8, 0xac,
0xc7, 0x4e, 0x91, 0x97, 0xb2, 0xd7, 0x0f, 0x0b, 0x1a, 0xa5, 0xb8, 0xff, 0x86, 0xaa, 0x3a, 0xf2,
0x6d, 0xd0, 0x9a, 0xdf, 0x72, 0x32, 0x32, 0x67, 0xaa, 0xab, 0xfc, 0xdc, 0xaf, 0x4f, 0xe4, 0x63,
0xf5, 0x0e, 0xea, 0x97, 0x2a, 0x6b, 0xd5, 0x0f, 0x42, 0x86, 0x96, 0xa6, 0x54, 0x78, 0xac, 0x99,
0x2f, 0xa7, 0x76, 0xbc, 0xbc, 0x03, 0x57, 0x69, 0x46, 0xe6, 0xfc, 0xb7, 0xa5, 0x43, 0xc8, 0x29,
0xbb, 0xe1, 0x7e, 0xd7, 0x58, 0x65, 0xce, 0x5e, 0xad, 0xa2, 0xbd, 0xae, 0xfa, 0x31, 0xe1, 0x19,
0x34, 0x2e, 0xd9, 0x4d, 0x3c, 0x4d, 0x02, 0xa9, 0x55, 0x53, 0xa7, 0x73, 0xc4, 0x1d, 0xb1, 0xf5,
0x25, 0xb4, 0x74, 0xae, 0x77, 0xf3, 0x2e, 0xdc, 0xd4, 0x38, 0x3d, 0xc1, 0xf9, 0x55, 0xd8, 0xd6,
0x41, 0x51, 0x8c, 0xe2, 0x44, 0xaa, 0x66, 0x56, 0x18, 0x7b, 0xdd, 0x52, 0x0b, 0xa7, 0x88, 0xc7,
0xa6, 0x56, 0x60, 0x1e, 0xe0, 0x91, 0x30, 0x05, 0x1b, 0x7e, 0xa2, 0x1d, 0x05, 0xc2, 0x95, 0x5c,
0xa4, 0x66, 0x5b, 0x0d, 0xc4, 0x19, 0x17, 0xf2, 0x9b, 0x4a, 0xbd, 0x62, 0x6f, 0x38, 0xbf, 0xb0,
0x74, 0xf4, 0x5e, 0x9a, 0x07, 0xac, 0x31, 0xcb, 0xc5, 0xba, 0xae, 0xb4, 0x5c, 0xd7, 0x0d, 0xe0,
0xf9, 0x48, 0x87, 0x61, 0x97, 0x25, 0xde, 0x28, 0xb8, 0xe1, 0xae, 0x98, 0x4e, 0x26, 0x28, 0x3b,
0x8f, 0xd8, 0x45, 0x68, 0x66, 0x41, 0x75, 0xfa, 0xcc, 0x90, 0x75, 0x35, 0xd5, 0xa9, 0x26, 0x1a,
0x68, 0x1a, 0xe7, 0xef, 0x2d, 0x6d, 0x0c, 0x26, 0x3d, 0x62, 0x6e, 0xb9, 0xe7, 0xf8, 0xf9, 0x0b,
0xa8, 0x9a, 0xd2, 0x4e, 0x97, 0xe5, 0x0b, 0x33, 0x94, 0x1c, 0xc3, 0xbd, 0xb3, 0xf9, 0xa4, 0x90,
0x9a, 0x4d, 0xce, 0xa7, 0xd0, 0xcc, 0xa1, 0x55, 0x9a, 0x1f, 0x1e, 0x0e, 0x8f, 0xbf, 0x1b, 0xea,
0x34, 0x7f, 0x46, 0xcf, 0x4f, 0xcf, 0x06, 0x7d, 0xdb, 0x52, 0xe9, 0x7a, 0xa8, 0xc0, 0xef, 0x8e,
0xe9, 0xd9, 0x9b, 0x9f, 0xda, 0x25, 0xe7, 0xfb, 0xb2, 0x9e, 0xa5, 0xe5, 0xcb, 0x05, 0x53, 0x05,
0xad, 0x11, 0x9e, 0x40, 0x45, 0xf9, 0x8f, 0x31, 0x26, 0xfc, 0xc6, 0x0b, 0xc9, 0xd8, 0x38, 0x78,
0x49, 0xc6, 0x68, 0x5c, 0xde, 0x08, 0x43, 0x50, 0x74, 0x95, 0xfa, 0xf8, 0x1c, 0x81, 0x2a, 0x31,
0xd3, 0x1f, 0x9d, 0xd4, 0xcc, 0x88, 0x38, 0xc3, 0x75, 0xd5, 0x0f, 0x38, 0x09, 0x17, 0x93, 0x38,
0x12, 0x69, 0x64, 0xcc, 0x60, 0x0c, 0xb2, 0x58, 0xb9, 0x07, 0x7a, 0xb3, 0xb6, 0xbf, 0x86, 0xc1,
0x74, 0x25, 0xe1, 0xab, 0x67, 0xb2, 0x75, 0xf5, 0xb2, 0xbf, 0x59, 0x7c, 0xd9, 0x15, 0xb7, 0xde,
0x5b, 0x51, 0x26, 0xaf, 0x9a, 0xe4, 0x6a, 0x1d, 0x36, 0xb2, 0xc6, 0xfb, 0xf7, 0x81, 0xac, 0x29,
0xb9, 0xf2, 0xba, 0x38, 0x19, 0x0c, 0xfb, 0x07, 0xc3, 0xaf, 0x4d, 0xc9, 0xd5, 0xeb, 0x0d, 0x4e,
0x50, 0x33, 0xba, 0xe4, 0x1a, 0xf4, 0xde, 0x1e, 0x0c, 0x07, 0x7d, 0xbb, 0x8c, 0x50, 0xaf, 0x3b,
0xec, 0x0d, 0xde, 0x0e, 0xfa, 0x76, 0xc5, 0xf9, 0x77, 0x4b, 0x77, 0xe4, 0xc5, 0x92, 0xb7, 0xcf,
0xbd, 0x40, 0xac, 0xff, 0x2d, 0xe6, 0x19, 0x34, 0xcc, 0x7b, 0x1e, 0xa4, 0x96, 0x36, 0x47, 0x90,
0x3f, 0x84, 0x2d, 0xdf, 0xec, 0x77, 0x0b, 0x96, 0xf7, 0xc9, 0xe2, 0x6c, 0x63, 0xd5, 0x91, 0x7b,
0xe9, 0x87, 0x79, 0x9e, 0xb6, 0x5f, 0x80, 0x9d, 0x8f, 0xa0, 0x5d, 0xa4, 0x28, 0x5c, 0xf6, 0x9d,
0xc2, 0x65, 0x2d, 0xe7, 0x5f, 0x4a, 0xb0, 0xb5, 0xf0, 0xbf, 0x0c, 0xeb, 0x73, 0xfe, 0xe2, 0x70,
0xb8, 0xb4, 0x34, 0x1c, 0x26, 0x1f, 0x01, 0xc9, 0x93, 0xb8, 0xf9, 0x29, 0x9b, 0x9d, 0x23, 0xd4,
0xb1, 0x2a, 0x5f, 0x44, 0x54, 0x1e, 0x52, 0x44, 0x90, 0xcf, 0xa1, 0x25, 0x62, 0x2f, 0x60, 0xa1,
0x1b, 0x06, 0xd1, 0x75, 0xfa, 0x0f, 0x24, 0x0b, 0xff, 0x96, 0x70, 0xaa, 0x28, 0xde, 0x22, 0x01,
0x6d, 0x8a, 0x39, 0x40, 0x7e, 0x0f, 0x76, 0x78, 0x24, 0xdc, 0xb4, 0x90, 0x74, 0xfd, 0xec, 0x5f,
0x46, 0xca, 0xcb, 0xb3, 0xcf, 0xa5, 0x4a, 0x95, 0x12, 0xbe, 0x88, 0x12, 0x8e, 0x00, 0xa0, 0xec,
0x36, 0xed, 0x67, 0x73, 0xd5, 0x9e, 0x55, 0xac, 0xf6, 0x0e, 0xa1, 0x69, 0x1a, 0x61, 0x6c, 0xc8,
0xd4, 0x13, 0xb6, 0xf7, 0x7f, 0x65, 0x7e, 0x62, 0x77, 0xfe, 0x2f, 0x46, 0x47, 0xe6, 0x3f, 0x8c,
0x0c, 0xd3, 0x3d, 0xd5, 0xf9, 0xe7, 0x77, 0x3b, 0x7f, 0x6b, 0x41, 0x1b, 0x45, 0xcc, 0x9d, 0xfc,
0xdb, 0xd0, 0x4c, 0x32, 0x28, 0x1d, 0x8e, 0xec, 0xe4, 0x06, 0x8a, 0xd9, 0x22, 0xcd, 0x13, 0x92,
0x7d, 0xd8, 0x11, 0xd3, 0x8b, 0x74, 0xaa, 0xf8, 0x8d, 0x88, 0xa3, 0xd7, 0x33, 0xc9, 0xd3, 0xe2,
0x6b, 0xe5, 0x1a, 0xf9, 0x08, 0xb6, 0xd3, 0x29, 0xf0, 0x7c, 0x83, 0x1e, 0x8d, 0x2f, 0x2f, 0x38,
0xbf, 0xb4, 0xb2, 0x42, 0x06, 0xf3, 0xa8, 0x6a, 0x42, 0x32, 0x13, 0xc3, 0xcf, 0x95, 0x99, 0xf2,
0x3d, 0xa8, 0x9a, 0xdf, 0x93, 0x74, 0x16, 0x30, 0x50, 0xde, 0x48, 0x2b, 0x05, 0x23, 0x7d, 0x06,
0x0d, 0x93, 0x79, 0x39, 0x9a, 0x45, 0x19, 0x8b, 0xc0, 0x0c, 0x31, 0xf7, 0xd7, 0x6a, 0xbe, 0xf8,
0xfd, 0xe7, 0x12, 0x6c, 0xe7, 0x44, 0xc3, 0x6e, 0x3e, 0x8e, 0xc8, 0xa7, 0x50, 0x65, 0xea, 0x4b,
0xc9, 0xd8, 0xde, 0x77, 0x56, 0x16, 0x06, 0x9a, 0x78, 0x4f, 0xff, 0xa1, 0x66, 0x07, 0xf9, 0x21,
0x6c, 0xc6, 0xa1, 0x6f, 0x48, 0xce, 0xb3, 0x7c, 0x53, 0x44, 0x9a, 0x6a, 0x05, 0x21, 0x33, 0x1e,
0x5d, 0x53, 0x7b, 0xa4, 0x54, 0x98, 0x7f, 0xab, 0x46, 0xba, 0x6d, 0xd8, 0x3c, 0x1c, 0xfc, 0xb4,
0xd7, 0xa5, 0x7d, 0xb7, 0xdb, 0xef, 0x2b, 0xd7, 0x26, 0xd0, 0xee, 0xf6, 0x7a, 0xc7, 0xe7, 0xc3,
0xb3, 0x53, 0x83, 0xb3, 0xb0, 0x95, 0x4e, 0xc9, 0xfa, 0x83, 0xb7, 0x03, 0x1d, 0xf0, 0x76, 0xc0,
0xce, 0x08, 0xe9, 0xe0, 0xe8, 0xf8, 0x5b, 0x15, 0xf8, 0x00, 0xaa, 0x6f, 0x8f, 0x7b, 0x87, 0x18,
0xf6, 0x30, 0x4a, 0x9c, 0x0f, 0x0d, 0xb4, 0x41, 0xb6, 0xa0, 0x79, 0x7e, 0xd0, 0x77, 0xcf, 0x4f,
0xfa, 0x5d, 0x64, 0x50, 0x25, 0x36, 0xb4, 0x86, 0xdd, 0xa3, 0x81, 0xdb, 0x7b, 0xd3, 0x1d, 0x7e,
0x3d, 0xe8, 0xdb, 0x35, 0xe7, 0x8f, 0x74, 0xfa, 0xcd, 0xb9, 0x1c, 0xf9, 0xf1, 0x82, 0x8f, 0x2e,
0xd9, 0xe2, 0x9c, 0xb8, 0xe8, 0x9e, 0x99, 0x92, 0x4a, 0x39, 0x25, 0xbd, 0xde, 0xfc, 0x83, 0xe6,
0xde, 0xc7, 0x9f, 0xa5, 0x9b, 0x2f, 0xaa, 0xea, 0xeb, 0x93, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff,
0x69, 0x10, 0xa1, 0xb7, 0xd8, 0x27, 0x00, 0x00,
0x56, 0xa3, 0x0f, 0xeb, 0xe3, 0x49, 0x96, 0xcb, 0x69, 0xcf, 0x58, 0xed, 0xf6, 0x6c, 0x77, 0x57,
0xef, 0xc4, 0x36, 0xc4, 0xe0, 0x01, 0x0f, 0xb0, 0xec, 0xf4, 0x4c, 0x0c, 0x6a, 0x49, 0xd3, 0xed,
0x76, 0x5b, 0x36, 0x69, 0x7b, 0x86, 0x25, 0x88, 0x28, 0xd2, 0x55, 0xd9, 0x56, 0xad, 0x4b, 0x55,
0xa2, 0x32, 0x65, 0xa3, 0x3d, 0x10, 0x40, 0x04, 0x67, 0x22, 0xb8, 0x2c, 0xc7, 0x39, 0x73, 0x24,
0x82, 0x03, 0x11, 0x1c, 0x38, 0x11, 0xfb, 0x1f, 0xe0, 0xca, 0x85, 0xe0, 0xc2, 0x8d, 0x03, 0x07,
0xe2, 0x65, 0x66, 0x95, 0xaa, 0xf4, 0x61, 0xec, 0xe0, 0xc4, 0xc9, 0xf5, 0x5e, 0xbe, 0x7c, 0xf9,
0x32, 0xdf, 0xf7, 0xb3, 0x60, 0x7d, 0xcc, 0xfc, 0xd8, 0x0f, 0xaf, 0xf6, 0xc7, 0x71, 0x24, 0x23,
0x52, 0x53, 0x7f, 0x2e, 0x27, 0xef, 0x77, 0xb7, 0xdc, 0x21, 0x93, 0x8e, 0xef, 0xf1, 0x50, 0xfa,
0x72, 0xaa, 0x97, 0x77, 0xb7, 0xc4, 0x34, 0x74, 0x1d, 0xc1, 0xa5, 0xf4, 0xc3, 0x2b, 0x61, 0x90,
0x36, 0x1b, 0x8f, 0x03, 0xdf, 0x65, 0xd2, 0x8f, 0x42, 0x67, 0xc4, 0x25, 0xf3, 0x98, 0x64, 0xce,
0x88, 0x0b, 0xc1, 0xae, 0xb8, 0xa1, 0xd9, 0x74, 0xa3, 0xd1, 0x68, 0x12, 0xfa, 0xd2, 0xe7, 0x66,
0x9b, 0xcd, 0xe0, 0xf1, 0x37, 0x5c, 0xba, 0x43, 0x3f, 0xbc, 0x7a, 0xc5, 0xdc, 0x6b, 0xee, 0x5d,
0x8c, 0x7b, 0x4c, 0xb2, 0x1e, 0x97, 0xcc, 0x0f, 0x04, 0x79, 0x02, 0x0d, 0xc5, 0x27, 0x9c, 0x8c,
0x2e, 0x79, 0xdc, 0x2e, 0x3c, 0x2d, 0xbc, 0x58, 0xa7, 0x80, 0xa8, 0x81, 0xc2, 0x90, 0x67, 0xd0,
0x94, 0x91, 0x64, 0x41, 0x42, 0x51, 0x54, 0x14, 0x0d, 0x85, 0xd3, 0x24, 0xf6, 0x7f, 0x57, 0xa0,
0x82, 0xbc, 0x27, 0x63, 0xb2, 0x0d, 0x6b, 0x6e, 0x10, 0xb9, 0xd7, 0x8a, 0x51, 0x99, 0x6a, 0x80,
0xb4, 0xa0, 0xe8, 0x7b, 0x6a, 0x67, 0x9d, 0x16, 0x7d, 0x8f, 0x7c, 0x0d, 0x35, 0x37, 0x0a, 0x25,
0x73, 0xa5, 0x68, 0x97, 0x9e, 0x96, 0x5e, 0x34, 0x0e, 0x9e, 0xef, 0x27, 0x2f, 0xb2, 0x7f, 0x36,
0x0d, 0xdd, 0xc3, 0x50, 0x48, 0x16, 0x04, 0xea, 0xae, 0x5d, 0x4d, 0xf9, 0xed, 0x01, 0x4d, 0x37,
0x91, 0x9f, 0x40, 0x23, 0x73, 0xd3, 0x76, 0x59, 0xf1, 0xd8, 0xc9, 0xf3, 0xe8, 0x1a, 0x82, 0x29,
0xcd, 0xd2, 0x92, 0x13, 0xd8, 0x48, 0xd8, 0x98, 0x37, 0x68, 0xaf, 0x3d, 0x2d, 0xbc, 0x68, 0x1c,
0x7c, 0x32, 0xdb, 0x7e, 0xc7, 0x83, 0xd1, 0xf9, 0xdd, 0xe4, 0x02, 0x48, 0x86, 0x7f, 0xc2, 0xb3,
0xf2, 0x10, 0x9e, 0x4b, 0x18, 0x90, 0xcf, 0xa1, 0x3a, 0x8e, 0xa3, 0xf7, 0x7e, 0xc0, 0xdb, 0x55,
0xc5, 0xeb, 0xd1, 0x8c, 0x57, 0xc2, 0xe3, 0x54, 0x13, 0xd0, 0x84, 0x92, 0x1c, 0x43, 0xcb, 0x7c,
0x26, 0x72, 0xd4, 0x1e, 0x22, 0xc7, 0xdc, 0x66, 0xf2, 0x19, 0x54, 0x8d, 0x11, 0xb6, 0xeb, 0x8a,
0xcf, 0x87, 0xf9, 0x27, 0x3e, 0xd3, 0x8b, 0x34, 0xa1, 0xc2, 0xc7, 0x4d, 0xac, 0x36, 0x11, 0x00,
0x1e, 0xf4, 0xb8, 0x73, 0xbb, 0x51, 0x82, 0x6b, 0x3e, 0x45, 0xe7, 0x69, 0x37, 0x96, 0x49, 0x70,
0xa4, 0x17, 0x69, 0x42, 0x85, 0x2f, 0x60, 0x3e, 0x13, 0x01, 0x9a, 0x0f, 0x7a, 0x81, 0xfc, 0x66,
0xd2, 0x01, 0xeb, 0x96, 0x49, 0x77, 0x78, 0x12, 0x06, 0xd3, 0x8e, 0xeb, 0x46, 0x93, 0x50, 0xb6,
0xd7, 0x97, 0x09, 0x62, 0x16, 0xe9, 0x02, 0x39, 0x71, 0x60, 0x67, 0x1e, 0x97, 0x88, 0xd6, 0x7a,
0x88, 0x68, 0xab, 0xb8, 0xd8, 0xff, 0x51, 0x86, 0xe6, 0xf1, 0x24, 0x90, 0x7e, 0x72, 0x22, 0x81,
0x72, 0xc8, 0x46, 0x5c, 0xf9, 0x60, 0x9d, 0xaa, 0x6f, 0xb2, 0x07, 0x75, 0xe9, 0x8f, 0xb8, 0x90,
0x6c, 0x34, 0x56, 0x9e, 0x58, 0xa2, 0x33, 0x04, 0xae, 0xea, 0x10, 0xe4, 0x46, 0x61, 0xbb, 0xa4,
0xb6, 0xcd, 0x10, 0xe4, 0x6b, 0x00, 0x37, 0x0a, 0xa2, 0xd8, 0x19, 0x32, 0x31, 0x34, 0xce, 0xf6,
0x74, 0x26, 0x74, 0xf6, 0xec, 0xfd, 0x2e, 0x12, 0xbe, 0x61, 0x62, 0x48, 0xeb, 0x6e, 0xf2, 0x49,
0x1e, 0xa1, 0xbf, 0x23, 0x03, 0xdf, 0x53, 0xce, 0x56, 0xa2, 0x55, 0x05, 0x1f, 0x7a, 0xe4, 0x47,
0xb0, 0x71, 0xcd, 0xa7, 0x2e, 0x8b, 0x3d, 0xc7, 0x84, 0x48, 0xe5, 0x3a, 0x75, 0xa5, 0x09, 0x44,
0x9f, 0x6a, 0x2c, 0xd9, 0x51, 0x96, 0xe0, 0x4c, 0x7c, 0x4f, 0xf9, 0x43, 0x9d, 0x56, 0xae, 0xf9,
0xf4, 0xc2, 0xf7, 0xc8, 0x97, 0x50, 0xf1, 0x47, 0xec, 0x8a, 0xa3, 0xad, 0xa3, 0x64, 0x3f, 0x5c,
0x21, 0xd9, 0xa1, 0x89, 0xb1, 0x87, 0x48, 0x4c, 0xcd, 0x1e, 0xf2, 0x19, 0x6c, 0xb9, 0x13, 0x21,
0xa3, 0x91, 0xff, 0x73, 0x1d, 0x59, 0x95, 0x60, 0xca, 0xdc, 0xeb, 0x94, 0xe4, 0x96, 0xd4, 0xd5,
0x76, 0x9f, 0x41, 0x3d, 0xbd, 0x23, 0x86, 0x3b, 0x3f, 0xf4, 0xf8, 0x9f, 0xb4, 0x0b, 0x4f, 0x4b,
0x2f, 0x4a, 0x54, 0x03, 0xbb, 0xff, 0x52, 0x80, 0xf5, 0xdc, 0x69, 0x59, 0xe1, 0x0b, 0x39, 0xe1,
0x13, 0x55, 0x15, 0x33, 0xaa, 0x6a, 0x43, 0x75, 0xcc, 0xa6, 0x41, 0xc4, 0x3c, 0xa5, 0x8a, 0x26,
0x4d, 0x40, 0x3c, 0xee, 0xd6, 0xf7, 0x24, 0xea, 0x00, 0x1f, 0x51, 0x03, 0xe4, 0x23, 0xa8, 0x0c,
0xb9, 0x7f, 0x35, 0x94, 0xe6, 0x6d, 0x0d, 0x44, 0x76, 0xa1, 0x86, 0xce, 0x2c, 0xfc, 0x9f, 0x73,
0xf5, 0xa6, 0x25, 0x9a, 0xc2, 0xe4, 0x39, 0xac, 0xc7, 0xea, 0xcb, 0x91, 0x2c, 0xbe, 0xe2, 0x52,
0xbd, 0x69, 0x89, 0x36, 0x35, 0xf2, 0x5c, 0xe1, 0x66, 0xc1, 0xbc, 0x96, 0x09, 0xe6, 0xf6, 0x2f,
0x8a, 0xb0, 0xf5, 0x2e, 0x72, 0x59, 0x60, 0x34, 0x73, 0x6a, 0x84, 0xfb, 0x2d, 0x28, 0x5f, 0xf3,
0xa9, 0x50, 0x4f, 0xd1, 0x38, 0x78, 0x36, 0xd3, 0xc2, 0x12, 0xe2, 0xfd, 0x23, 0x3e, 0xa5, 0x8a,
0x9c, 0x7c, 0x01, 0xcd, 0x11, 0xaa, 0x89, 0x19, 0xef, 0x2a, 0x2a, 0x9f, 0xf8, 0x68, 0xb9, 0x12,
0x69, 0x8e, 0x16, 0x6f, 0x38, 0x66, 0x42, 0xdc, 0x46, 0xb1, 0x67, 0xac, 0x36, 0x85, 0xf1, 0x15,
0x31, 0xb5, 0x1e, 0xf1, 0xa9, 0x7a, 0xad, 0x3a, 0x4d, 0x40, 0xf2, 0x22, 0x35, 0x39, 0x23, 0x94,
0xce, 0x00, 0x75, 0x3a, 0x8f, 0xde, 0xfd, 0x35, 0x28, 0xe1, 0x86, 0x65, 0xfe, 0x44, 0xa0, 0x8c,
0x49, 0x52, 0x89, 0xdb, 0xa4, 0xea, 0xdb, 0xfe, 0x87, 0x02, 0x7c, 0x98, 0xbb, 0x2c, 0xe7, 0xf1,
0x1b, 0x1e, 0x04, 0x11, 0x5a, 0xb9, 0xb1, 0x6e, 0xe7, 0x86, 0xc7, 0xc2, 0x8f, 0x42, 0xc5, 0x6c,
0x8d, 0xb6, 0x0c, 0xfa, 0x5b, 0x8d, 0x45, 0x43, 0x19, 0x73, 0xae, 0x1c, 0x45, 0x73, 0xae, 0x20,
0x78, 0xe8, 0xa9, 0x3c, 0xcd, 0x6f, 0x7c, 0x97, 0x3b, 0x4a, 0x14, 0x7d, 0x5b, 0xd0, 0xa8, 0x01,
0x0a, 0x34, 0x23, 0x90, 0xd3, 0x31, 0x37, 0x77, 0x36, 0x04, 0xe7, 0xd3, 0xb1, 0x8a, 0x00, 0xc2,
0xbf, 0x0a, 0x99, 0x9c, 0xc4, 0x5c, 0x5d, 0xb8, 0x49, 0x67, 0x08, 0xfb, 0xfb, 0x02, 0x58, 0x28,
0x76, 0x36, 0xf3, 0xae, 0xc8, 0xe6, 0x3f, 0x82, 0x0d, 0x3f, 0x43, 0xe5, 0xa4, 0xa9, 0xbd, 0x95,
0x45, 0xe7, 0x64, 0x56, 0x22, 0x95, 0x16, 0x44, 0x4a, 0x1e, 0xb6, 0x9c, 0xb7, 0xfe, 0xe4, 0x89,
0xd6, 0x54, 0xa9, 0x91, 0x80, 0xf6, 0xbf, 0x17, 0x60, 0x67, 0x45, 0x71, 0x70, 0xcf, 0xba, 0xe3,
0x39, 0xac, 0x9b, 0x0c, 0xe7, 0x28, 0xf7, 0x37, 0x22, 0x35, 0x0d, 0x52, 0xfb, 0xea, 0x23, 0xa8,
0xf1, 0x50, 0x38, 0x19, 0xc1, 0xaa, 0x3c, 0x14, 0xea, 0x8d, 0x9f, 0x41, 0x33, 0x60, 0x42, 0x3a,
0x93, 0xb1, 0xc7, 0x24, 0xd7, 0xb1, 0xac, 0x4c, 0x1b, 0x88, 0xbb, 0xd0, 0x28, 0xbc, 0xb3, 0x98,
0x0a, 0xc9, 0x47, 0x8e, 0x64, 0x57, 0x58, 0x06, 0x94, 0xf0, 0xce, 0x1a, 0x75, 0xce, 0xae, 0x04,
0xf9, 0x04, 0x5a, 0x01, 0xda, 0x88, 0x13, 0xfa, 0xee, 0xb5, 0x3a, 0x44, 0x87, 0xb3, 0x75, 0x85,
0x1d, 0x18, 0xa4, 0xfd, 0xe7, 0x15, 0x78, 0xb4, 0xb2, 0x12, 0x22, 0xbf, 0x0e, 0xdb, 0x59, 0x41,
0x1c, 0xb5, 0x37, 0x98, 0x9a, 0xdb, 0x93, 0x8c, 0x40, 0xef, 0xf4, 0xca, 0xff, 0xe3, 0xa7, 0x40,
0xdd, 0x32, 0xcf, 0xe3, 0x9e, 0x0a, 0xca, 0x35, 0xaa, 0x01, 0xb4, 0x93, 0x4b, 0x54, 0x32, 0xf7,
0x54, 0x89, 0x51, 0xa3, 0x09, 0x88, 0xf4, 0xa3, 0x09, 0xca, 0xd4, 0xd0, 0xf4, 0x0a, 0x40, 0xfa,
0x98, 0x8f, 0xa2, 0x1b, 0xee, 0xa9, 0x8a, 0xa0, 0x46, 0x13, 0x90, 0x3c, 0x85, 0xe6, 0x90, 0x09,
0x47, 0xb1, 0x75, 0x26, 0x42, 0xe5, 0xf7, 0x1a, 0x85, 0x21, 0x13, 0x1d, 0x44, 0x5d, 0xa8, 0x24,
0x71, 0xc3, 0x63, 0xff, 0x7d, 0x52, 0x7d, 0x0b, 0xc9, 0xe4, 0x44, 0xa7, 0xef, 0x12, 0x25, 0xd9,
0xa5, 0x33, 0xb5, 0xa2, 0x8a, 0xe6, 0x78, 0x22, 0x64, 0x42, 0xb9, 0xa1, 0x28, 0x1b, 0x0a, 0x67,
0x48, 0xbe, 0x82, 0xc7, 0xa6, 0x92, 0x74, 0x62, 0xfe, 0xc7, 0x13, 0x2e, 0xa4, 0xd6, 0xa2, 0xda,
0xc2, 0xdb, 0x96, 0xda, 0xd1, 0x36, 0x24, 0x54, 0x53, 0x28, 0x65, 0xe2, 0x7e, 0xbe, 0x7a, 0xbb,
0x76, 0x83, 0xcd, 0x95, 0xdb, 0xbb, 0xca, 0x33, 0xbe, 0x86, 0xbd, 0xf9, 0xed, 0xf8, 0x1c, 0x92,
0x9b, 0xe3, 0x89, 0xda, 0xff, 0x28, 0xbf, 0x9f, 0x2a, 0x0a, 0x7d, 0xfe, 0x6a, 0x06, 0x5a, 0x80,
0xad, 0xd5, 0x0c, 0xb4, 0x04, 0xcf, 0xa0, 0xe9, 0xf9, 0x62, 0x1c, 0xb0, 0xa9, 0xb6, 0xaf, 0x6d,
0xa5, 0xfa, 0x86, 0xc1, 0xa1, 0x8d, 0xd9, 0xb7, 0x8b, 0xfe, 0x9e, 0x94, 0x38, 0xcb, 0xfd, 0x7d,
0xc1, 0xa8, 0x8b, 0x4b, 0x8c, 0x7a, 0xde, 0x72, 0x4b, 0x0b, 0x96, 0x6b, 0xbf, 0x82, 0xdd, 0xf9,
0x83, 0x4f, 0x27, 0x97, 0x81, 0xef, 0x76, 0x87, 0xec, 0x9e, 0xb1, 0xc6, 0xfe, 0xfb, 0x12, 0xac,
0xe7, 0xda, 0x90, 0xff, 0x75, 0x5f, 0x53, 0x39, 0xe6, 0x13, 0x68, 0x8c, 0x63, 0xff, 0x86, 0x49,
0xee, 0x5c, 0xf3, 0xa9, 0xa9, 0x00, 0xc0, 0xa0, 0x30, 0x1b, 0x3d, 0xc5, 0xa8, 0x2a, 0xdc, 0xd8,
0x1f, 0xa3, 0x5c, 0xca, 0x2f, 0x9b, 0x34, 0x8b, 0xc2, 0x82, 0xe0, 0x67, 0x91, 0x1f, 0x1a, 0xaf,
0xac, 0x51, 0x03, 0x61, 0xba, 0xd4, 0xb6, 0xca, 0x3d, 0x55, 0x10, 0xd4, 0x68, 0x0a, 0xcf, 0x9c,
0xa6, 0x9a, 0x75, 0x9a, 0x13, 0xb0, 0x8c, 0x76, 0x85, 0x23, 0x23, 0x07, 0xf9, 0x98, 0x2a, 0xeb,
0x93, 0x55, 0xcd, 0x96, 0x21, 0x3f, 0x8f, 0xde, 0x46, 0x7e, 0x48, 0x5b, 0x71, 0x0e, 0x26, 0x2f,
0xa1, 0x96, 0x94, 0xf8, 0xa6, 0xa5, 0x78, 0xb2, 0x82, 0x91, 0xe9, 0x2d, 0x04, 0x4d, 0x37, 0x60,
0x06, 0xe3, 0xa1, 0x1b, 0x4f, 0xc7, 0x32, 0x75, 0xfa, 0x19, 0x42, 0xe5, 0xb7, 0x31, 0x77, 0x25,
0x9b, 0xb9, 0xfe, 0x0c, 0x81, 0x49, 0xcb, 0x90, 0xa2, 0x03, 0xab, 0x42, 0xa5, 0xa9, 0x5e, 0xae,
0x35, 0x43, 0x1f, 0xf1, 0xa9, 0xc0, 0xf2, 0xe6, 0xf1, 0x1d, 0x37, 0x32, 0xfa, 0x2a, 0xa4, 0xfa,
0xfa, 0x18, 0x60, 0xac, 0x6c, 0x43, 0xa9, 0x4b, 0xeb, 0xbf, 0xae, 0x31, 0xa8, 0xad, 0x54, 0xe9,
0xa5, 0xac, 0xd2, 0xef, 0x08, 0xac, 0x3b, 0xba, 0x6e, 0x49, 0x4a, 0xe5, 0x3a, 0xad, 0x20, 0x78,
0xe8, 0xa1, 0xdd, 0x26, 0x6d, 0xe2, 0x14, 0x57, 0x2b, 0x5a, 0xf1, 0x29, 0xee, 0x50, 0x29, 0x51,
0xbb, 0x6f, 0x55, 0x1f, 0xa6, 0x00, 0xf2, 0x0d, 0x6c, 0xc6, 0xfc, 0x86, 0xb3, 0x80, 0x7b, 0x8e,
0xa9, 0x9c, 0x92, 0x5a, 0x39, 0xd3, 0x53, 0x52, 0x43, 0x92, 0x36, 0x32, 0x71, 0x1e, 0x21, 0xec,
0xbf, 0x2e, 0x82, 0x35, 0xef, 0x16, 0xe4, 0xab, 0x4c, 0x2b, 0xbf, 0x50, 0xf9, 0xad, 0x48, 0x60,
0x99, 0x46, 0xfe, 0x35, 0x34, 0xcd, 0xeb, 0xe1, 0x2d, 0x45, 0xbb, 0x38, 0x5f, 0xc2, 0xaf, 0xf6,
0x43, 0xda, 0x18, 0xa7, 0xdf, 0x82, 0xbc, 0x84, 0x6a, 0x52, 0x41, 0x96, 0x94, 0x5d, 0xdd, 0x21,
0x46, 0x72, 0xc5, 0x64, 0xc7, 0xff, 0x61, 0x9c, 0x60, 0xff, 0x18, 0x36, 0xd4, 0x2a, 0x0a, 0x64,
0xf2, 0xc9, 0xfd, 0xe2, 0xc3, 0x97, 0xb0, 0x9d, 0x6c, 0x3c, 0xd6, 0x33, 0x1c, 0x41, 0x39, 0xbb,
0xef, 0xee, 0xdf, 0x85, 0x8f, 0x74, 0xd7, 0x29, 0xfd, 0x1b, 0x5f, 0x4e, 0xbb, 0x3c, 0x94, 0x3c,
0xbe, 0x63, 0xbf, 0x05, 0x25, 0xdf, 0xd3, 0xcf, 0xdb, 0xa4, 0xf8, 0x69, 0xf7, 0x74, 0x8c, 0xcb,
0x73, 0xe8, 0xb8, 0x2e, 0x57, 0xce, 0x74, 0x5f, 0x2e, 0x7d, 0xed, 0x2c, 0x79, 0x2e, 0x3d, 0x5f,
0x8c, 0x7c, 0x21, 0x1e, 0xc0, 0xc6, 0x81, 0xe7, 0x8b, 0x6c, 0x06, 0x91, 0xcc, 0xe5, 0x55, 0x8e,
0xbe, 0x96, 0x54, 0x3c, 0x4c, 0x1a, 0x9e, 0x75, 0x83, 0xe9, 0x48, 0xf4, 0x2a, 0x4c, 0xe4, 0x82,
0xf3, 0x50, 0x3d, 0x55, 0x8d, 0x56, 0x87, 0x4c, 0x9c, 0x71, 0x1e, 0xda, 0x7f, 0x55, 0x80, 0x27,
0x77, 0x9f, 0x20, 0x48, 0x00, 0x1f, 0x33, 0xb3, 0xec, 0xb8, 0x6a, 0xdd, 0x09, 0xb3, 0x04, 0xc6,
0xbe, 0x5f, 0xcc, 0x37, 0xfe, 0xab, 0x38, 0xd2, 0xc7, 0x6c, 0xf5, 0x69, 0xf6, 0x3f, 0xd6, 0xe1,
0x07, 0x77, 0xef, 0x5f, 0x08, 0x35, 0x0b, 0x3d, 0x7c, 0x39, 0xdb, 0xc3, 0xbf, 0x87, 0xcd, 0xac,
0xb8, 0xb3, 0x9a, 0xbb, 0x75, 0xf0, 0x93, 0xfb, 0x8a, 0xbc, 0x9f, 0x05, 0xb0, 0x44, 0xa7, 0x56,
0x38, 0x87, 0xc9, 0x06, 0xa8, 0x72, 0x2e, 0x40, 0x11, 0x28, 0xc7, 0x9c, 0x25, 0x49, 0x47, 0x7d,
0xa3, 0xc8, 0x5e, 0x62, 0x0d, 0x26, 0xe7, 0xcc, 0x10, 0x98, 0x90, 0x98, 0xb1, 0x38, 0x93, 0x77,
0x52, 0x18, 0xeb, 0x35, 0x33, 0xdb, 0x54, 0xed, 0x67, 0x93, 0x26, 0x20, 0xa6, 0x37, 0x36, 0x91,
0xc3, 0xb4, 0x4b, 0x37, 0x90, 0xee, 0x69, 0xc7, 0xc1, 0x34, 0x99, 0x89, 0xaa, 0x14, 0xd1, 0xc4,
0x9e, 0x76, 0x1c, 0x4c, 0x8d, 0x8f, 0x2d, 0x44, 0xd1, 0x86, 0x2e, 0x3b, 0xb2, 0x51, 0xf4, 0x3d,
0x6c, 0x8e, 0xf8, 0xe8, 0x92, 0xc7, 0x62, 0xe8, 0x8f, 0x93, 0x0a, 0xae, 0xf9, 0xc0, 0x87, 0x3c,
0x4e, 0x39, 0xe8, 0x7a, 0x8f, 0x5a, 0xa3, 0x39, 0x0c, 0xf9, 0x8b, 0xc2, 0xac, 0x86, 0x5b, 0x56,
0x5e, 0xae, 0xab, 0x23, 0x5f, 0xdd, 0xfb, 0xc8, 0xa4, 0x3d, 0x58, 0x28, 0x47, 0xd3, 0x32, 0x6c,
0x71, 0x09, 0x9f, 0xd9, 0xe3, 0x01, 0x47, 0x0d, 0xb4, 0xb4, 0xcb, 0x18, 0x70, 0xce, 0xd9, 0x36,
0xe6, 0x9c, 0xcd, 0xfe, 0xcf, 0x02, 0x58, 0xf3, 0xd6, 0x42, 0x00, 0x2a, 0x83, 0x08, 0xbf, 0xac,
0x0f, 0xc8, 0x06, 0x34, 0x06, 0xfc, 0xf6, 0x24, 0xe4, 0xe7, 0xd1, 0x49, 0xc8, 0xad, 0x02, 0xd9,
0x81, 0xad, 0x01, 0xbf, 0x3d, 0xd5, 0x95, 0xcc, 0xeb, 0x38, 0x9a, 0x8c, 0x31, 0xf8, 0x59, 0x45,
0xd2, 0x80, 0xea, 0x31, 0x0f, 0x91, 0x89, 0x55, 0x22, 0x75, 0x58, 0xa3, 0xa8, 0x30, 0xab, 0x4c,
0x08, 0xb4, 0xba, 0xb9, 0xfa, 0xd1, 0x5a, 0x43, 0x26, 0x69, 0x24, 0x3e, 0x0c, 0x6f, 0x7c, 0xa9,
0x0e, 0xb7, 0x2a, 0x64, 0x1b, 0xac, 0xf9, 0x94, 0x6d, 0x55, 0xc9, 0x0f, 0x60, 0x37, 0xc5, 0xce,
0x54, 0x92, 0xac, 0xd7, 0xc8, 0x16, 0x6c, 0xa4, 0xeb, 0x47, 0x3e, 0xb6, 0x0f, 0x56, 0x5d, 0x9f,
0xb1, 0xf0, 0x60, 0x16, 0xd8, 0x7f, 0x59, 0x00, 0x6b, 0x5e, 0xb1, 0xa4, 0x0d, 0xdb, 0xf3, 0xb8,
0x43, 0x2f, 0xc0, 0x17, 0x78, 0x0c, 0x3b, 0xf3, 0x2b, 0xa7, 0x3c, 0xf4, 0xfc, 0xf0, 0xca, 0x2a,
0x90, 0x3d, 0x68, 0xcf, 0x2f, 0x26, 0xd1, 0xd7, 0x2a, 0x2e, 0x5b, 0xed, 0x71, 0x37, 0xc0, 0x32,
0xce, 0x2a, 0xd9, 0x7f, 0x56, 0x80, 0x47, 0x2b, 0xb5, 0x8d, 0xcf, 0x79, 0x11, 0x5e, 0x87, 0xd1,
0x6d, 0x68, 0x7d, 0x80, 0xc0, 0xec, 0xcc, 0x26, 0xd4, 0x32, 0x67, 0x34, 0xa1, 0x36, 0xe3, 0x49,
0xd6, 0xa1, 0xde, 0x65, 0xa1, 0xcb, 0x83, 0x80, 0x7b, 0x56, 0x19, 0xf7, 0x9d, 0x63, 0xb7, 0xc2,
0x3d, 0x6b, 0x8d, 0x6c, 0xc2, 0xfa, 0x45, 0xa8, 0xc0, 0xef, 0xa2, 0x58, 0x0e, 0xa7, 0x56, 0xc5,
0xfe, 0xbe, 0x00, 0x4d, 0xb4, 0xc7, 0x57, 0x51, 0x74, 0x3d, 0x62, 0xf1, 0xf5, 0xea, 0x50, 0x3f,
0x89, 0x03, 0x93, 0xb8, 0xf0, 0x33, 0xed, 0xf9, 0x4b, 0x99, 0x9e, 0xff, 0x31, 0xd4, 0x55, 0xbd,
0xee, 0x20, 0xad, 0x0e, 0x2a, 0x35, 0x85, 0xb8, 0x88, 0x83, 0x6c, 0xe3, 0xb6, 0x96, 0x6f, 0xdc,
0x3e, 0x06, 0x30, 0xc6, 0x8a, 0x16, 0x5a, 0xd1, 0x16, 0x6a, 0x30, 0x1d, 0x69, 0xff, 0x29, 0x7c,
0x88, 0x12, 0xf6, 0x43, 0x71, 0x21, 0x78, 0x8c, 0x07, 0xe9, 0x89, 0xe9, 0x0a, 0x51, 0x77, 0xa1,
0x36, 0x31, 0x74, 0x46, 0xde, 0x14, 0x56, 0x03, 0xcc, 0x21, 0xf3, 0xd5, 0xac, 0x43, 0x17, 0x72,
0x55, 0x05, 0x1f, 0xe6, 0xfa, 0xca, 0x72, 0x4e, 0x3c, 0xfb, 0xad, 0x2e, 0x97, 0xba, 0x01, 0x67,
0xf1, 0x1b, 0x5f, 0xc8, 0x28, 0x9e, 0x66, 0x83, 0x67, 0x21, 0x17, 0x3c, 0x3f, 0x06, 0x70, 0x91,
0x50, 0xdf, 0xc5, 0x04, 0x77, 0x83, 0xe9, 0x48, 0xfb, 0x97, 0x05, 0x20, 0xc8, 0xcc, 0x4c, 0xfc,
0x4f, 0x7d, 0x57, 0x4e, 0x62, 0xbe, 0x74, 0x32, 0x95, 0x19, 0x1f, 0x16, 0x57, 0x8c, 0x0f, 0x4b,
0x6a, 0xb0, 0xb2, 0x30, 0x3e, 0x2c, 0x2b, 0x74, 0x32, 0x3e, 0x7c, 0x0c, 0x75, 0xd5, 0x49, 0xa9,
0xf9, 0xa1, 0x1e, 0xc5, 0xa8, 0xf9, 0xe1, 0xd9, 0xd2, 0xf9, 0x61, 0x45, 0x11, 0xac, 0x98, 0x1f,
0x56, 0xb3, 0xf3, 0xc3, 0x21, 0x6c, 0x2d, 0xde, 0x44, 0xac, 0x1e, 0x91, 0xfe, 0x0e, 0xd4, 0xc6,
0x86, 0xc8, 0x94, 0x87, 0x7b, 0xf9, 0x90, 0x98, 0xe7, 0x44, 0x53, 0x6a, 0xfb, 0x97, 0x45, 0x68,
0x64, 0x66, 0xf3, 0x2b, 0xf4, 0xde, 0x86, 0x2a, 0xf3, 0xbc, 0x98, 0x0b, 0x91, 0xbc, 0x97, 0x01,
0xb3, 0x22, 0x95, 0x72, 0x22, 0xe5, 0x6b, 0x7e, 0xdd, 0x81, 0x65, 0x6a, 0x7e, 0x02, 0xe5, 0x31,
0x93, 0x43, 0x53, 0xbf, 0xab, 0xef, 0x54, 0x53, 0x95, 0x8c, 0xa6, 0xb2, 0x63, 0xf1, 0xaa, 0x99,
0x51, 0x9a, 0xb1, 0xf8, 0x36, 0xac, 0xf1, 0x51, 0xf4, 0x33, 0x5f, 0xe5, 0xbe, 0x3a, 0xd5, 0x00,
0xaa, 0xea, 0x96, 0x05, 0x01, 0x97, 0x66, 0x14, 0x62, 0x20, 0x64, 0x8e, 0x66, 0x64, 0x7a, 0x22,
0xf5, 0xad, 0xd4, 0xea, 0x7b, 0x1e, 0x0f, 0x4d, 0x2f, 0x64, 0xa0, 0x3b, 0xe6, 0x20, 0xbb, 0x50,
0x1b, 0x47, 0xc2, 0x57, 0x5d, 0xe5, 0xba, 0x9e, 0x17, 0x27, 0xb0, 0xfd, 0x6f, 0xe6, 0x29, 0xcd,
0xff, 0x5b, 0x56, 0x3c, 0x65, 0xe6, 0xc1, 0x8a, 0x4b, 0xc7, 0xdc, 0xa5, 0xfc, 0x04, 0x35, 0x33,
0xa9, 0x54, 0xdf, 0x6a, 0x28, 0xc0, 0x63, 0xff, 0x86, 0x7b, 0xce, 0xfb, 0x38, 0x1a, 0x99, 0x17,
0x6c, 0x18, 0xdc, 0x37, 0x71, 0x34, 0x22, 0x2f, 0x61, 0x57, 0xb7, 0xef, 0x82, 0x7b, 0x8e, 0x5a,
0x30, 0x53, 0x48, 0x35, 0x87, 0xd7, 0x41, 0x60, 0x47, 0x35, 0xf3, 0x82, 0x7b, 0xbd, 0x74, 0xfd,
0x10, 0x97, 0xf5, 0x48, 0x2a, 0x74, 0x13, 0xf6, 0xfa, 0xd1, 0x41, 0xa3, 0x14, 0xf7, 0xdf, 0x50,
0x15, 0x49, 0xb6, 0x45, 0x5a, 0xf1, 0x7f, 0x9e, 0x94, 0x0c, 0xb7, 0x98, 0xb9, 0x31, 0xb6, 0xb4,
0xa5, 0xa5, 0xff, 0xa3, 0xc2, 0x55, 0x9a, 0x92, 0x65, 0x75, 0x00, 0xf9, 0x98, 0xf1, 0x5f, 0x05,
0x1d, 0x34, 0xce, 0xd8, 0x0d, 0xf7, 0x3a, 0xc6, 0x0e, 0x33, 0x16, 0x5a, 0xc8, 0x5b, 0xe8, 0xb2,
0x7f, 0x1f, 0xec, 0x41, 0xfd, 0x3d, 0xbb, 0x89, 0x26, 0xb1, 0x2f, 0xf5, 0x83, 0xd7, 0xe8, 0x0c,
0x71, 0x47, 0x34, 0x7d, 0x06, 0x4d, 0x9d, 0xdd, 0x9d, 0xac, 0xd3, 0x36, 0x34, 0x4e, 0xcf, 0x6c,
0x7e, 0x15, 0x36, 0x75, 0x18, 0x14, 0xc3, 0x28, 0x96, 0xaa, 0x7d, 0x15, 0xc6, 0x42, 0x37, 0xd4,
0xc2, 0x19, 0xe2, 0xb1, 0x8d, 0x15, 0x18, 0xf9, 0x79, 0x28, 0x4c, 0x89, 0x86, 0x9f, 0x68, 0x1d,
0xbe, 0x70, 0x24, 0x17, 0x89, 0xa1, 0x56, 0x7c, 0x71, 0xce, 0x85, 0x7c, 0x5b, 0xae, 0x95, 0xad,
0x35, 0xfb, 0x17, 0x05, 0x1d, 0xaf, 0x17, 0x26, 0x00, 0x2b, 0x8c, 0x6d, 0xbe, 0x92, 0x2b, 0x2e,
0x56, 0x72, 0x7d, 0x78, 0x32, 0xd4, 0x81, 0xd7, 0x61, 0xb1, 0x3b, 0xf4, 0x6f, 0xb8, 0x23, 0x26,
0xe3, 0x31, 0xca, 0xce, 0x43, 0x76, 0x19, 0x98, 0xe9, 0x4f, 0x8d, 0xee, 0x19, 0xb2, 0x8e, 0xa6,
0x3a, 0xd3, 0x44, 0x7d, 0x4d, 0x63, 0xff, 0x5d, 0x41, 0x37, 0x79, 0x26, 0x21, 0x62, 0x36, 0xb9,
0xe7, 0xc0, 0xf9, 0x2b, 0xa8, 0x98, 0x62, 0x4e, 0x17, 0xe2, 0x73, 0x53, 0x93, 0x0c, 0xc3, 0xfd,
0xf3, 0xd9, 0x6c, 0x90, 0x9a, 0x4d, 0xf6, 0x17, 0xd0, 0xc8, 0xa0, 0x55, 0x62, 0x1f, 0x1c, 0x0d,
0x4e, 0xbe, 0x1b, 0xe8, 0xc4, 0x7e, 0x4e, 0x2f, 0xce, 0xce, 0xfb, 0x3d, 0xab, 0xa0, 0x12, 0xf4,
0x40, 0x81, 0xdf, 0x9d, 0xd0, 0xf3, 0x37, 0x3f, 0xb5, 0x8a, 0xf6, 0xf7, 0x25, 0x3d, 0x3d, 0xcb,
0x16, 0x08, 0xa6, 0xee, 0x59, 0x21, 0x3c, 0x81, 0xb2, 0xf2, 0x0a, 0x63, 0x4c, 0xf8, 0x8d, 0x17,
0x92, 0x91, 0x71, 0xdb, 0xa2, 0x8c, 0xd0, 0xb8, 0xdc, 0x21, 0x06, 0x9d, 0xf0, 0x2a, 0xf1, 0xdc,
0x19, 0x02, 0x55, 0x62, 0xe6, 0x3d, 0x3a, 0x8d, 0x99, 0xa1, 0x70, 0x8a, 0xeb, 0xa8, 0x7f, 0xd9,
0xc4, 0x5c, 0x8c, 0xa3, 0x50, 0x24, 0xb1, 0x30, 0x85, 0x31, 0xac, 0x62, 0xad, 0xee, 0xeb, 0xcd,
0xda, 0xfe, 0xea, 0x06, 0xd3, 0x91, 0x84, 0x2f, 0x9f, 0xc2, 0xd6, 0xd4, 0xcb, 0xfe, 0x66, 0xfe,
0x65, 0x97, 0xdc, 0x7a, 0x7f, 0x49, 0x61, 0xbc, 0x6c, 0x76, 0xab, 0x75, 0x58, 0x4f, 0x5b, 0xed,
0xdf, 0x07, 0xb2, 0xa2, 0xc8, 0xca, 0xea, 0xe2, 0xb4, 0x3f, 0xe8, 0x1d, 0x0e, 0x5e, 0x9b, 0x22,
0xab, 0xdb, 0xed, 0x9f, 0xa2, 0x66, 0x74, 0x91, 0xd5, 0xef, 0xbe, 0x3b, 0x1c, 0xf4, 0x7b, 0x56,
0x09, 0xa1, 0x6e, 0x67, 0xd0, 0xed, 0xbf, 0xeb, 0xf7, 0xac, 0xb2, 0xfd, 0xaf, 0x05, 0xdd, 0x83,
0xe7, 0x8b, 0xdc, 0x1e, 0x77, 0x7d, 0xb1, 0xfa, 0xbf, 0x2f, 0x7b, 0x50, 0x37, 0xef, 0x79, 0x98,
0x58, 0xda, 0x0c, 0x41, 0xfe, 0x10, 0x36, 0x3c, 0xb3, 0xdf, 0xc9, 0x59, 0xde, 0xe7, 0xf3, 0xd3,
0x8c, 0x65, 0x47, 0xee, 0x27, 0x1f, 0xe6, 0x79, 0x5a, 0x5e, 0x0e, 0xb6, 0x3f, 0x85, 0x56, 0x9e,
0x22, 0x77, 0xd9, 0x0f, 0x72, 0x97, 0x2d, 0xd8, 0xff, 0x5c, 0x84, 0x8d, 0xb9, 0x5f, 0x2a, 0xac,
0xce, 0xf2, 0xf3, 0xe3, 0xe0, 0xe2, 0xc2, 0x38, 0x98, 0x7c, 0x0a, 0x24, 0x4b, 0xe2, 0x64, 0xe7,
0x6a, 0x56, 0x86, 0x50, 0xc7, 0xaa, 0x6c, 0xd9, 0x50, 0x7e, 0x48, 0xd9, 0x40, 0xbe, 0x84, 0xa6,
0x88, 0x5c, 0x9f, 0x05, 0x4e, 0xe0, 0x87, 0xd7, 0xc9, 0xcf, 0x43, 0x1e, 0xcd, 0xfd, 0xf4, 0x41,
0x51, 0xbc, 0x43, 0x02, 0xda, 0x10, 0x33, 0x80, 0xfc, 0x1e, 0x6c, 0xf3, 0x50, 0x38, 0x49, 0xe9,
0xe8, 0x78, 0xe9, 0x0f, 0x42, 0x4a, 0x8b, 0xd3, 0xce, 0x85, 0xda, 0x94, 0x12, 0x3e, 0x8f, 0x12,
0xb6, 0x00, 0xa0, 0xec, 0x36, 0xe9, 0x60, 0x33, 0xf5, 0x5d, 0x21, 0x5f, 0xdf, 0x1d, 0x41, 0xc3,
0xb4, 0xbe, 0xd8, 0x82, 0xa9, 0x27, 0x6c, 0x1d, 0xfc, 0xca, 0xec, 0xc4, 0xce, 0xec, 0x07, 0x44,
0xc7, 0xe6, 0xf7, 0x43, 0x86, 0xe9, 0xbe, 0xea, 0xf5, 0xb3, 0xbb, 0xed, 0xbf, 0x2d, 0x40, 0x0b,
0x45, 0xcc, 0x9c, 0xfc, 0xdb, 0xd0, 0x88, 0x53, 0x28, 0x19, 0x87, 0x6c, 0x67, 0x46, 0x88, 0xe9,
0x22, 0xcd, 0x12, 0x92, 0x03, 0xd8, 0x16, 0x93, 0xcb, 0x64, 0x8e, 0xf8, 0x56, 0x44, 0xe1, 0xab,
0xa9, 0xe4, 0x49, 0xb9, 0xb5, 0x74, 0x8d, 0x7c, 0x0a, 0x9b, 0xc9, 0xdc, 0x77, 0xb6, 0x41, 0x0f,
0xc3, 0x17, 0x17, 0xec, 0xbf, 0x29, 0xa4, 0xe5, 0x09, 0x66, 0x58, 0xd5, 0x76, 0xa4, 0x26, 0x86,
0x9f, 0x4b, 0x33, 0xe5, 0x47, 0x50, 0x31, 0xff, 0x41, 0xd2, 0x59, 0xc0, 0x40, 0x59, 0x23, 0x2d,
0xe7, 0x8c, 0x74, 0x0f, 0xea, 0x26, 0xf3, 0x72, 0x34, 0x8b, 0x12, 0x96, 0x7d, 0x29, 0x62, 0xe6,
0xaf, 0x95, 0x6c, 0xb9, 0xfb, 0x4f, 0x45, 0xd8, 0xcc, 0x88, 0x86, 0xfd, 0x7b, 0x14, 0x92, 0x2f,
0xa0, 0xc2, 0xd4, 0x97, 0x92, 0xb1, 0x75, 0x60, 0x2f, 0x2d, 0x19, 0x34, 0xf1, 0xbe, 0xfe, 0x43,
0xcd, 0x0e, 0xf2, 0x43, 0x58, 0x8f, 0x02, 0xcf, 0x90, 0x5c, 0xa4, 0xf9, 0x26, 0x8f, 0x34, 0xbf,
0x9c, 0x41, 0xc8, 0x0c, 0x44, 0x57, 0x54, 0x25, 0x09, 0x15, 0xe6, 0xdf, 0x8a, 0x91, 0x6e, 0x13,
0xd6, 0x8f, 0xfa, 0x3f, 0xed, 0x76, 0x68, 0xcf, 0xe9, 0xf4, 0x7a, 0xca, 0xb5, 0x09, 0xb4, 0x3a,
0xdd, 0xee, 0xc9, 0xc5, 0xe0, 0xfc, 0xcc, 0xe0, 0x0a, 0xd8, 0x3c, 0x27, 0x64, 0xbd, 0xfe, 0xbb,
0xbe, 0x0e, 0x78, 0xdb, 0x60, 0xa5, 0x84, 0xb4, 0x7f, 0x7c, 0xf2, 0xad, 0x0a, 0x7c, 0x00, 0x95,
0x77, 0x27, 0xdd, 0x23, 0x0c, 0x7b, 0x18, 0x25, 0x2e, 0x06, 0x06, 0x5a, 0x23, 0x1b, 0xd0, 0xb8,
0x38, 0xec, 0x39, 0x17, 0xa7, 0xbd, 0x0e, 0x32, 0xa8, 0x10, 0x0b, 0x9a, 0x83, 0xce, 0x71, 0xdf,
0xe9, 0xbe, 0xe9, 0x0c, 0x5e, 0xf7, 0x7b, 0x56, 0xd5, 0xfe, 0x23, 0x9d, 0x7e, 0x33, 0x2e, 0x47,
0x7e, 0x3c, 0xe7, 0xa3, 0x0b, 0xb6, 0x38, 0x23, 0xce, 0xbb, 0x67, 0xaa, 0xa4, 0x62, 0x46, 0x49,
0xaf, 0xd6, 0xff, 0xa0, 0xb1, 0xff, 0xd9, 0xcb, 0x64, 0xf3, 0x65, 0x45, 0x7d, 0x7d, 0xfe, 0x3f,
0x01, 0x00, 0x00, 0xff, 0xff, 0x9a, 0xb1, 0xaf, 0x0c, 0xb6, 0x27, 0x00, 0x00,
}

View File

@ -28,8 +28,8 @@ message Backup {
FetchingBackedUpDataDetails profileDetails = 8;
SyncSetting setting = 9;
FetchingBackedUpDataDetails settingsDetails = 10;
SyncKeypairFull fullKeypair = 11;
FetchingBackedUpDataDetails fullKeypairDetails = 12;
SyncKeypair keypair = 11;
FetchingBackedUpDataDetails keypairDetails = 12;
SyncAccount watchOnlyAccount = 13;
FetchingBackedUpDataDetails watchOnlyAccountDetails = 14;
}
@ -296,6 +296,7 @@ message SyncAccount {
bool chat = 10;
bool hidden = 11;
bool removed = 12;
int64 position = 13;
}
message SyncKeypair {
@ -307,11 +308,8 @@ message SyncKeypair {
uint64 last_used_derivation_index = 6;
string synced_from = 7;
repeated SyncAccount accounts = 8;
}
message SyncKeypairFull {
SyncKeypair keypair = 1;
repeated SyncKeycard keycards = 2;
repeated SyncKeycard keycards = 9;
bool removed = 10;
}
message SyncSavedAddress {

View File

@ -326,8 +326,6 @@ func (m *StatusMessage) HandleApplication() error {
return m.unmarshalProtobufData(new(protobuf.SyncEnsUsernameDetail))
case protobuf.ApplicationMetadataMessage_SYNC_KEYPAIR:
return m.unmarshalProtobufData(new(protobuf.SyncKeypair))
case protobuf.ApplicationMetadataMessage_SYNC_FULL_KEYPAIR:
return m.unmarshalProtobufData(new(protobuf.SyncKeypairFull))
case protobuf.ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_NOTIFICATION:
return m.unmarshalProtobufData(new(protobuf.SyncActivityCenterNotifications))
case protobuf.ApplicationMetadataMessage_SYNC_ACTIVITY_CENTER_NOTIFICATION_STATE:

View File

@ -13,7 +13,6 @@ type WakuBackedUpDataResponse struct {
FetchingDataProgress map[string]protobuf.FetchingBackedUpDataDetails // key represents the data/section backup details refer to
Profile *BackedUpProfile
Setting *settings.SyncSettingField
Keycards []*accounts.Keycard
Keypair *accounts.Keypair
WatchOnlyAccount *accounts.Account
}
@ -24,14 +23,12 @@ func (sfwr *WakuBackedUpDataResponse) MarshalJSON() ([]byte, error) {
FetchingDataProgress map[string]FetchingBackupedDataDetails `json:"fetchingBackedUpDataProgress,omitempty"`
Profile *BackedUpProfile `json:"backedUpProfile,omitempty"`
Setting *settings.SyncSettingField `json:"backedUpSettings,omitempty"`
Keycards []*accounts.Keycard `json:"backedUpKeycards,omitempty"`
Keypair *accounts.Keypair `json:"backedUpKeypair,omitempty"`
WatchOnlyAccount *accounts.Account `json:"backedUpWatchOnlyAccount,omitempty"`
}{
Clock: sfwr.Clock,
Profile: sfwr.Profile,
Setting: sfwr.Setting,
Keycards: sfwr.Keycards,
Keypair: sfwr.Keypair,
WatchOnlyAccount: sfwr.WatchOnlyAccount,
}

View File

@ -78,7 +78,7 @@ func (api *API) UpdateKeypairName(ctx context.Context, keyUID string, name strin
}
func (api *API) UpdateAccountPosition(ctx context.Context, address types.Address, position int64) error {
return api.db.UpdateAccountPosition(address, position)
return (*api.messenger).UpdateAccountPosition(address, position)
}
func (api *API) GetAccounts(ctx context.Context) ([]*accounts.Account, error) {
@ -102,66 +102,17 @@ func (api *API) GetKeypairByKeyUID(ctx context.Context, keyUID string) (*account
}
func (api *API) DeleteAccount(ctx context.Context, address types.Address) error {
acc, err := api.db.GetAccountByAddress(address)
err := (*api.messenger).DeleteAccount(address)
if err != nil {
return err
}
if acc.Type != accounts.AccountTypeWatch {
kp, err := api.db.GetKeypairByKeyUID(acc.KeyUID)
if err != nil {
return err
}
lastAcccountOfKeypairWithTheSameKey := len(kp.Accounts) == 1
knownKeycardsForKeyUID, err := api.db.GetKeycardByKeyUID(acc.KeyUID)
if err != nil {
return err
}
if len(knownKeycardsForKeyUID) == 0 {
err = api.manager.DeleteAccount(address)
var e *account.ErrCannotLocateKeyFile
if err != nil && !errors.As(err, &e) {
return err
}
if acc.Type != accounts.AccountTypeKey {
if lastAcccountOfKeypairWithTheSameKey {
err = api.manager.DeleteAccount(types.Address(common.HexToAddress(kp.DerivedFrom)))
var e *account.ErrCannotLocateKeyFile
if err != nil && !errors.As(err, &e) {
return err
}
}
}
} else {
if lastAcccountOfKeypairWithTheSameKey {
knownKeycards, err := api.db.GetAllKnownKeycards()
if err != nil {
return err
}
for _, kc := range knownKeycards {
if kc.KeyUID == acc.KeyUID {
clock := uint64(time.Now().Unix())
err = (*api.messenger).RemoveMigratedAccountsForKeycard(ctx, kc.KeycardUID, kc.AccountsAddresses, clock)
if err != nil {
return err
}
}
}
}
}
}
api.feed.Send(accountsevent.Event{
Type: accountsevent.EventTypeRemoved,
Accounts: []common.Address{common.Address(address)},
})
return (*api.messenger).DeleteAccount(address)
return nil
}
func (api *API) AddKeypair(ctx context.Context, password string, keypair *accounts.Keypair) error {

View File

@ -161,10 +161,6 @@ func (m *MessengerSignalsHandler) SendWakuBackedUpKeypair(response *wakusync.Wak
signal.SendWakuBackedUpKeypair(response)
}
func (m *MessengerSignalsHandler) SendWakuBackedUpKeycards(response *wakusync.WakuBackedUpDataResponse) {
signal.SendWakuBackedUpKeycards(response)
}
func (m *MessengerSignalsHandler) SendWakuBackedUpWatchOnlyAccount(response *wakusync.WakuBackedUpDataResponse) {
signal.SendWakuBackedUpWatchOnlyAccount(response)
}

View File

@ -79,7 +79,7 @@ func setupTestAPI(t *testing.T) (*API, func()) {
accounts := []*accounts.Account{
{Address: types.HexToAddress(utils.TestConfig.Account1.WalletAddress), Chat: true, Wallet: true},
}
require.NoError(t, service.accountsDB.SaveOrUpdateAccounts(accounts))
require.NoError(t, service.accountsDB.SaveOrUpdateAccounts(accounts, false))
require.NoError(t, service.accountsDB.CreateSettings(settings, *nodeConfig))

View File

@ -15,9 +15,6 @@ const (
// EventWakuBackedUpKeypair is emitted while applying fetched keypair data from waku
EventWakuBackedUpKeypair = "waku.backedup.keypair"
// EventWakuBackedUpKeycards is emitted while applying fetched keycard data from waku
EventWakuBackedUpKeycards = "waku.backedup.keycards"
// EventWakuBackedUpWatchOnlyAccount is emitted while applying fetched watch only account data from waku
EventWakuBackedUpWatchOnlyAccount = "waku.backedup.watch-only-account" // #nosec G101
)
@ -38,10 +35,6 @@ func SendWakuBackedUpKeypair(obj json.Marshaler) {
send(EventWakuBackedUpKeypair, obj)
}
func SendWakuBackedUpKeycards(obj json.Marshaler) {
send(EventWakuBackedUpKeycards, obj)
}
func SendWakuBackedUpWatchOnlyAccount(obj json.Marshaler) {
send(EventWakuBackedUpWatchOnlyAccount, obj)
}