Populate colorHash and colorId (#2630)

* feat: add colorId utility

it returns color id for given pubkey

* feat: populate Account with colorHash and colorId

accounts displayed to users on login page should display colorHash and
avatar fallback color (aka colorId)
This commit is contained in:
osmaczko 2022-04-08 19:54:29 +02:00 committed by GitHub
parent dd86a82a0f
commit 89626d997e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 92 additions and 14 deletions

View File

@ -27,6 +27,7 @@ import (
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/profiling"
protocol "github.com/status-im/status-go/protocol"
identityUtils "github.com/status-im/status-go/protocol/identity"
"github.com/status-im/status-go/protocol/identity/alias"
"github.com/status-im/status-go/protocol/identity/colorhash"
"github.com/status-im/status-go/protocol/identity/emojihash"
@ -277,6 +278,18 @@ func SaveAccountAndLogin(accountData, password, settingsJSON, configJSON, subacc
if err != nil {
return makeJSONResponse(err)
}
for _, acc := range subaccs {
if acc.Chat {
colorHash, _ := colorhash.GenerateFor(string(acc.PublicKey.Bytes()))
colorID, _ := identityUtils.ToColorID(string(acc.PublicKey.Bytes()))
account.ColorHash = colorHash
account.ColorID = colorID
break
}
}
api.RunAsync(func() error {
log.Debug("starting a node, and saving account with configuration", "key-uid", account.KeyUID)
err := statusBackend.StartNodeWithAccountAndInitialConfig(account, password, settings, &conf, subaccs)
@ -664,6 +677,10 @@ func ColorHash(pk string) string {
return prepareJSONResponse(colorhash.GenerateFor(pk))
}
func ColorID(pk string) string {
return prepareJSONResponse(identityUtils.ToColorID(pk))
}
func ValidateMnemonic(mnemonic string) string {
m := extkeys.NewMnemonic()
err := m.ValidateMnemonic(mnemonic, extkeys.Language(0))

View File

@ -3,6 +3,7 @@ package multiaccounts
import (
"context"
"database/sql"
"encoding/json"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/images"
@ -15,6 +16,8 @@ type Account struct {
Name string `json:"name"`
Timestamp int64 `json:"timestamp"`
Identicon string `json:"identicon"`
ColorHash [][]int `json:"colorHash"`
ColorID int64 `json:"colorId"`
KeycardPairing string `json:"keycard-pairing"`
KeyUID string `json:"key-uid"`
Images []images.IdentityImage `json:"images"`
@ -47,7 +50,7 @@ func (db *Database) Close() error {
}
func (db *Database) GetAccounts() (rst []Account, err error) {
rows, err := db.db.Query("SELECT a.name, a.loginTimestamp, a.identicon, a.keycardPairing, a.keyUid, ii.name, ii.image_payload, ii.width, ii.height, ii.file_size, ii.resize_target FROM accounts AS a LEFT JOIN identity_images AS ii ON ii.key_uid = a.keyUid ORDER BY loginTimestamp DESC")
rows, err := db.db.Query("SELECT a.name, a.loginTimestamp, a.identicon, a.colorHash, a.colorId, a.keycardPairing, a.keyUid, ii.name, ii.image_payload, ii.width, ii.height, ii.file_size, ii.resize_target FROM accounts AS a LEFT JOIN identity_images AS ii ON ii.key_uid = a.keyUid ORDER BY loginTimestamp DESC")
if err != nil {
return nil, err
}
@ -59,6 +62,8 @@ func (db *Database) GetAccounts() (rst []Account, err error) {
acc := Account{}
accLoginTimestamp := sql.NullInt64{}
accIdenticon := sql.NullString{}
accColorHash := sql.NullString{}
accColorID := sql.NullInt64{}
ii := &images.IdentityImage{}
iiName := sql.NullString{}
iiWidth := sql.NullInt64{}
@ -70,6 +75,8 @@ func (db *Database) GetAccounts() (rst []Account, err error) {
&acc.Name,
&accLoginTimestamp,
&accIdenticon,
&accColorHash,
&accColorID,
&acc.KeycardPairing,
&acc.KeyUID,
&iiName,
@ -85,6 +92,11 @@ func (db *Database) GetAccounts() (rst []Account, err error) {
acc.Timestamp = accLoginTimestamp.Int64
acc.Identicon = accIdenticon.String
acc.ColorID = accColorID.Int64
err = json.Unmarshal([]byte(accColorHash.String), &acc.ColorHash)
if err != nil {
return nil, err
}
ii.KeyUID = acc.KeyUID
ii.Name = iiName.String
@ -121,12 +133,20 @@ func (db *Database) GetAccounts() (rst []Account, err error) {
}
func (db *Database) SaveAccount(account Account) error {
_, err := db.db.Exec("INSERT OR REPLACE INTO accounts (name, identicon, keycardPairing, keyUid) VALUES (?, ?, ?, ?)", account.Name, account.Identicon, account.KeycardPairing, account.KeyUID)
colorHash, err := json.Marshal(account.ColorHash)
if err != nil {
return err
}
_, err = db.db.Exec("INSERT OR REPLACE INTO accounts (name, identicon, colorHash, colorId, keycardPairing, keyUid) VALUES (?, ?, ?, ?, ?, ?)", account.Name, account.Identicon, colorHash, account.ColorID, account.KeycardPairing, account.KeyUID)
return err
}
func (db *Database) UpdateAccount(account Account) error {
_, err := db.db.Exec("UPDATE accounts SET name = ?, identicon = ?, keycardPairing = ? WHERE keyUid = ?", account.Name, account.Identicon, account.KeycardPairing, account.KeyUID)
colorHash, err := json.Marshal(account.ColorHash)
if err != nil {
return err
}
_, err = db.db.Exec("UPDATE accounts SET name = ?, identicon = ?, colorHash = ?, colorId = ?, keycardPairing = ? WHERE keyUid = ?", account.Name, account.Identicon, colorHash, account.ColorID, account.KeycardPairing, account.KeyUID)
return err
}

View File

@ -25,7 +25,7 @@ func setupTestDB(t *testing.T) (*Database, func()) {
func TestAccounts(t *testing.T) {
db, stop := setupTestDB(t)
defer stop()
expected := Account{Name: "string", KeyUID: "string"}
expected := Account{Name: "string", KeyUID: "string", ColorHash: [][]int{{4, 3}, {4, 0}, {4, 3}, {4, 0}}, ColorID: 10}
require.NoError(t, db.SaveAccount(expected))
accounts, err := db.GetAccounts()
require.NoError(t, err)
@ -36,7 +36,7 @@ func TestAccounts(t *testing.T) {
func TestAccountsUpdate(t *testing.T) {
db, stop := setupTestDB(t)
defer stop()
expected := Account{KeyUID: "string"}
expected := Account{KeyUID: "string", ColorHash: [][]int{{4, 3}, {4, 0}, {4, 3}, {4, 0}}, ColorID: 10}
require.NoError(t, db.SaveAccount(expected))
expected.Name = "chars"
require.NoError(t, db.UpdateAccount(expected))
@ -154,7 +154,7 @@ func TestDatabase_GetAccountsWithIdentityImages(t *testing.T) {
{Name: "string", KeyUID: keyUID2 + "2"},
{Name: "string", KeyUID: keyUID2 + "3"},
}
expected := `[{"name":"string","timestamp":100,"identicon":"data","keycard-pairing":"","key-uid":"0xdeadbeef","images":[{"keyUid":"0xdeadbeef","type":"large","uri":"data:image/png;base64,iVBORw0KGgoAAAANSUg=","width":240,"height":300,"fileSize":1024,"resizeTarget":240},{"keyUid":"0xdeadbeef","type":"thumbnail","uri":"data:image/jpeg;base64,/9j/2wCEAFA3PEY8MlA=","width":80,"height":80,"fileSize":256,"resizeTarget":80}]},{"name":"string","timestamp":10,"identicon":"","keycard-pairing":"","key-uid":"0x1337beef","images":null},{"name":"string","timestamp":0,"identicon":"","keycard-pairing":"","key-uid":"0x1337beef2","images":null},{"name":"string","timestamp":0,"identicon":"","keycard-pairing":"","key-uid":"0x1337beef3","images":[{"keyUid":"0x1337beef3","type":"large","uri":"data:image/png;base64,iVBORw0KGgoAAAANSUg=","width":240,"height":300,"fileSize":1024,"resizeTarget":240},{"keyUid":"0x1337beef3","type":"thumbnail","uri":"data:image/jpeg;base64,/9j/2wCEAFA3PEY8MlA=","width":80,"height":80,"fileSize":256,"resizeTarget":80}]}]`
expected := `[{"name":"string","timestamp":100,"identicon":"data","colorHash":null,"colorId":0,"keycard-pairing":"","key-uid":"0xdeadbeef","images":[{"keyUid":"0xdeadbeef","type":"large","uri":"data:image/png;base64,iVBORw0KGgoAAAANSUg=","width":240,"height":300,"fileSize":1024,"resizeTarget":240},{"keyUid":"0xdeadbeef","type":"thumbnail","uri":"data:image/jpeg;base64,/9j/2wCEAFA3PEY8MlA=","width":80,"height":80,"fileSize":256,"resizeTarget":80}]},{"name":"string","timestamp":10,"identicon":"","colorHash":null,"colorId":0,"keycard-pairing":"","key-uid":"0x1337beef","images":null},{"name":"string","timestamp":0,"identicon":"","colorHash":null,"colorId":0,"keycard-pairing":"","key-uid":"0x1337beef2","images":null},{"name":"string","timestamp":0,"identicon":"","colorHash":null,"colorId":0,"keycard-pairing":"","key-uid":"0x1337beef3","images":[{"keyUid":"0x1337beef3","type":"large","uri":"data:image/png;base64,iVBORw0KGgoAAAANSUg=","width":240,"height":300,"fileSize":1024,"resizeTarget":240},{"keyUid":"0x1337beef3","type":"thumbnail","uri":"data:image/jpeg;base64,/9j/2wCEAFA3PEY8MlA=","width":80,"height":80,"fileSize":256,"resizeTarget":80}]}]`
for _, a := range testAccs {
require.NoError(t, db.SaveAccount(a))

View File

@ -6,6 +6,7 @@
// 1605007189_identity_images.up.sql (268B)
// 1606224181_drop_photo_path_from_accounts.down.sql (892B)
// 1606224181_drop_photo_path_from_accounts.up.sql (866B)
// 1649317600_add_color_hash.up.sql (201B)
// doc.go (74B)
package migrations
@ -90,7 +91,7 @@ func _0001_accountsDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "0001_accounts.down.sql", size: 21, mode: os.FileMode(0664), modTime: time.Unix(1649064826, 0)}
info := bindataFileInfo{name: "0001_accounts.down.sql", size: 21, mode: os.FileMode(0664), modTime: time.Unix(1649174047, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd2, 0x61, 0x4c, 0x18, 0xfc, 0xc, 0xdf, 0x5c, 0x1f, 0x5e, 0xd3, 0xbd, 0xfa, 0x12, 0x5e, 0x8d, 0x8d, 0x8b, 0xb9, 0x5f, 0x99, 0x46, 0x63, 0xa5, 0xe3, 0xa6, 0x8a, 0x4, 0xf1, 0x73, 0x8a, 0xe9}}
return a, nil
}
@ -110,7 +111,7 @@ func _0001_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "0001_accounts.up.sql", size: 163, mode: os.FileMode(0664), modTime: time.Unix(1649064826, 0)}
info := bindataFileInfo{name: "0001_accounts.up.sql", size: 163, mode: os.FileMode(0664), modTime: time.Unix(1649174047, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf2, 0xfa, 0x99, 0x8e, 0x96, 0xb3, 0x13, 0x6c, 0x1f, 0x6, 0x27, 0xc5, 0xd2, 0xd4, 0xe0, 0xa5, 0x26, 0x82, 0xa7, 0x26, 0xf2, 0x68, 0x9d, 0xed, 0x9c, 0x3d, 0xbb, 0xdc, 0x37, 0x28, 0xbc, 0x1}}
return a, nil
}
@ -130,7 +131,7 @@ func _1605007189_identity_imagesDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1605007189_identity_images.down.sql", size: 29, mode: os.FileMode(0664), modTime: time.Unix(1649064826, 0)}
info := bindataFileInfo{name: "1605007189_identity_images.down.sql", size: 29, mode: os.FileMode(0664), modTime: time.Unix(1649174047, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2f, 0xcf, 0xa7, 0xae, 0xd5, 0x4f, 0xcd, 0x14, 0x63, 0x9, 0xbe, 0x39, 0x49, 0x18, 0x96, 0xb2, 0xa3, 0x8, 0x7d, 0x41, 0xdb, 0x50, 0x5d, 0xf5, 0x4d, 0xa2, 0xd, 0x8f, 0x57, 0x79, 0x77, 0x67}}
return a, nil
}
@ -150,7 +151,7 @@ func _1605007189_identity_imagesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1605007189_identity_images.up.sql", size: 268, mode: os.FileMode(0664), modTime: time.Unix(1649064826, 0)}
info := bindataFileInfo{name: "1605007189_identity_images.up.sql", size: 268, mode: os.FileMode(0664), modTime: time.Unix(1649174047, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x50, 0xb6, 0xc1, 0x5c, 0x76, 0x72, 0x6b, 0x22, 0x34, 0xdc, 0x96, 0xdc, 0x2b, 0xfd, 0x2d, 0xbe, 0xcc, 0x1e, 0xd4, 0x5, 0x93, 0xd, 0xc2, 0x51, 0xf3, 0x1a, 0xef, 0x2b, 0x26, 0xa4, 0xeb, 0x65}}
return a, nil
}
@ -170,7 +171,7 @@ func _1606224181_drop_photo_path_from_accountsDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1606224181_drop_photo_path_from_accounts.down.sql", size: 892, mode: os.FileMode(0664), modTime: time.Unix(1649064826, 0)}
info := bindataFileInfo{name: "1606224181_drop_photo_path_from_accounts.down.sql", size: 892, mode: os.FileMode(0664), modTime: time.Unix(1649174047, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x90, 0x24, 0x17, 0x7, 0x80, 0x93, 0x6f, 0x8d, 0x5d, 0xaa, 0x8c, 0x79, 0x15, 0x5d, 0xb3, 0x19, 0xd7, 0xd8, 0x39, 0xf9, 0x3a, 0x63, 0x8f, 0x81, 0x15, 0xb6, 0xd6, 0x9a, 0x37, 0xa8, 0x8e, 0x9b}}
return a, nil
}
@ -190,11 +191,31 @@ func _1606224181_drop_photo_path_from_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1606224181_drop_photo_path_from_accounts.up.sql", size: 866, mode: os.FileMode(0664), modTime: time.Unix(1649064826, 0)}
info := bindataFileInfo{name: "1606224181_drop_photo_path_from_accounts.up.sql", size: 866, mode: os.FileMode(0664), modTime: time.Unix(1649174047, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xff, 0x4c, 0x97, 0xee, 0xef, 0x82, 0xb8, 0x6c, 0x71, 0xbb, 0x50, 0x7b, 0xe6, 0xd9, 0x22, 0x31, 0x7c, 0x1a, 0xfe, 0x91, 0x28, 0xf6, 0x6, 0x36, 0xe, 0xb1, 0xf1, 0xc8, 0x25, 0xac, 0x7e, 0xd6}}
return a, nil
}
var __1649317600_add_color_hashUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\x4c\x4e\xce\x2f\xcd\x2b\x29\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x48\xce\xcf\xc9\x2f\xf2\x48\x2c\xce\x50\x08\x71\x8d\x08\x51\xf0\xf3\x0f\x51\xf0\x0b\xf5\xf1\x51\x70\x71\x75\x73\x0c\xf5\x09\x51\x50\x52\xb2\xe6\x22\xca\x0c\xcf\x14\x05\x4f\x3f\x2c\x06\x18\x58\x73\x85\x06\xb8\x38\x86\x20\x69\x0d\x76\x0d\x41\xb2\xd7\x16\x6c\x07\x4e\x35\x9e\x29\x0a\xb6\x20\x43\x00\x01\x00\x00\xff\xff\xfa\xaf\xaf\xd9\xc9\x00\x00\x00")
func _1649317600_add_color_hashUpSqlBytes() ([]byte, error) {
return bindataRead(
__1649317600_add_color_hashUpSql,
"1649317600_add_color_hash.up.sql",
)
}
func _1649317600_add_color_hashUpSql() (*asset, error) {
bytes, err := _1649317600_add_color_hashUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1649317600_add_color_hash.up.sql", size: 201, mode: os.FileMode(0664), modTime: time.Unix(1649417138, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1a, 0xf, 0x37, 0x6d, 0xcf, 0x99, 0xc9, 0x2e, 0xdc, 0x70, 0x11, 0xb4, 0x36, 0x26, 0x4f, 0x39, 0xa8, 0x44, 0xf, 0xcb, 0xcc, 0x81, 0x74, 0x7a, 0x88, 0xaa, 0x54, 0x8c, 0xc4, 0xe, 0x56, 0x4f}}
return a, nil
}
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
func docGoBytes() ([]byte, error) {
@ -210,7 +231,7 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0664), modTime: time.Unix(1649064826, 0)}
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0664), modTime: time.Unix(1649174047, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x7c, 0x28, 0xcd, 0x47, 0xf2, 0xfa, 0x7c, 0x51, 0x2d, 0xd8, 0x38, 0xb, 0xb0, 0x34, 0x9d, 0x4c, 0x62, 0xa, 0x9e, 0x28, 0xc3, 0x31, 0x23, 0xd9, 0xbb, 0x89, 0x9f, 0xa0, 0x89, 0x1f, 0xe8}}
return a, nil
}
@ -318,6 +339,8 @@ var _bindata = map[string]func() (*asset, error){
"1606224181_drop_photo_path_from_accounts.up.sql": _1606224181_drop_photo_path_from_accountsUpSql,
"1649317600_add_color_hash.up.sql": _1649317600_add_color_hashUpSql,
"doc.go": docGo,
}
@ -368,7 +391,8 @@ var _bintree = &bintree{nil, map[string]*bintree{
"1605007189_identity_images.up.sql": &bintree{_1605007189_identity_imagesUpSql, map[string]*bintree{}},
"1606224181_drop_photo_path_from_accounts.down.sql": &bintree{_1606224181_drop_photo_path_from_accountsDownSql, map[string]*bintree{}},
"1606224181_drop_photo_path_from_accounts.up.sql": &bintree{_1606224181_drop_photo_path_from_accountsUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
"1649317600_add_color_hash.up.sql": &bintree{_1649317600_add_color_hashUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory.

View File

@ -0,0 +1,4 @@
ALTER TABLE accounts ADD COLUMN colorHash TEXT NOT NULL DEFAULT "";
ALTER TABLE accounts ADD COLUMN colorId INT NOT NULL DEFAULT 0;
UPDATE accounts SET colorHash = "";
UPDATE accounts SET colorId = 0;

View File

@ -9,6 +9,19 @@ import (
"github.com/ethereum/go-ethereum/crypto/secp256k1"
)
func ToColorID(pubkey string) (int64, error) {
const colorPalletLength = 12
pubkeyValue, ok := new(big.Int).SetString(pubkey, 0)
if !ok {
return 0, fmt.Errorf("invalid pubkey: %s", pubkey)
}
colorID := new(big.Int).Mod(pubkeyValue, new(big.Int).SetInt64(colorPalletLength-1)).Int64()
return colorID, nil
}
func ToBigBase(value *big.Int, base uint64) (res [](uint64)) {
toBigBaseImpl(value, base, &res)
return