Replace address with keyUid in accounts db

Account's address was used as a primary key in accounts db and as a
deterministic id of an account in some API calls. Also it was used as a
part of the name of the account specific database. This revealed some
extra information about the account and wasn't necessary.
At first the hash of the address was planned to be used as a
deterministic id, but we already have a keyUid which is calculated as
sha256 hash of account's public key and has similar properties:
- it is deterministic
- doesn't reveal accounts public key or address in plain
This commit is contained in:
Roman Volosovskyi 2019-12-05 10:00:57 +02:00
parent c2e3f381ad
commit 4c0d8dedea
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
12 changed files with 58 additions and 62 deletions

View File

@ -1 +1 @@
0.36.1
0.36.2

View File

@ -1,6 +1,7 @@
package api
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
@ -515,7 +516,7 @@ func TestBackendGetVerifiedAccount(t *testing.T) {
backend := NewGethStatusBackend()
backend.UpdateRootDataDir(tmpdir)
require.NoError(t, backend.AccountManager().InitKeystore(filepath.Join(tmpdir, "keystore")))
require.NoError(t, backend.ensureAppDBOpened(multiaccounts.Account{Address: common.Address{1, 1, 1}}, password))
require.NoError(t, backend.ensureAppDBOpened(multiaccounts.Account{KeyUID: "0x1"}, password))
config, err := params.NewNodeConfig(tmpdir, 178733)
require.NoError(t, err)
// this is for StatusNode().Config() call inside of the getVerifiedWalletAccount
@ -566,8 +567,10 @@ func TestLoginWithKey(t *testing.T) {
b := NewGethStatusBackend()
pkey, err := crypto.GenerateKey()
require.NoError(t, err)
keyUIDHex := sha256.Sum256(crypto.FromECDSAPub(&pkey.PublicKey))
keyUID := types.EncodeHex(keyUIDHex[:])
main := multiaccounts.Account{
Address: crypto.PubkeyToAddress(pkey.PublicKey),
KeyUID: keyUID,
}
tmpdir, err := ioutil.TempDir("", "login-with-key-test-")
require.NoError(t, err)

View File

@ -178,7 +178,7 @@ func (b *GethStatusBackend) ensureAppDBOpened(account multiaccounts.Account, pas
if len(b.rootDataDir) == 0 {
return errors.New("root datadir wasn't provided")
}
path := filepath.Join(b.rootDataDir, fmt.Sprintf("app-%x.sql", account.Address))
path := filepath.Join(b.rootDataDir, fmt.Sprintf("app-%x.sql", account.KeyUID))
b.appDB, err = appdatabase.InitializeDB(path, password)
if err != nil {
return err
@ -235,7 +235,7 @@ func (b *GethStatusBackend) startNodeWithKey(acc multiaccounts.Account, password
if err != nil {
return err
}
err = b.multiaccountsDB.UpdateAccountTimestamp(acc.Address, time.Now().Unix())
err = b.multiaccountsDB.UpdateAccountTimestamp(acc.KeyUID, time.Now().Unix())
if err != nil {
return err
}
@ -291,7 +291,7 @@ func (b *GethStatusBackend) startNodeWithAccount(acc multiaccounts.Account, pass
if err != nil {
return err
}
err = b.multiaccountsDB.UpdateAccountTimestamp(acc.Address, time.Now().Unix())
err = b.multiaccountsDB.UpdateAccountTimestamp(acc.KeyUID, time.Now().Unix())
if err != nil {
return err
}

View File

@ -52,7 +52,7 @@ var (
func buildAccountData(name, chatAddress string) *C.char {
return C.CString(fmt.Sprintf(`{
"name": "%s",
"address": "%s"
"key-uid": "%s"
}`, name, chatAddress))
}

View File

@ -326,13 +326,13 @@ func Login(accountData, password string) string {
return makeJSONResponse(err)
}
api.RunAsync(func() error {
log.Debug("start a node with account", "address", account.Address)
log.Debug("start a node with account", "key-uid", account.KeyUID)
err := statusBackend.StartNodeWithAccount(account, password)
if err != nil {
log.Error("failed to start a node", "address", account.Address, "error", err)
log.Error("failed to start a node", "key-uid", account.KeyUID, "error", err)
return err
}
log.Debug("started a node with", "address", account.Address)
log.Debug("started a node with", "key-uid", account.KeyUID)
return nil
})
return makeJSONResponse(nil)
@ -356,13 +356,13 @@ func SaveAccountAndLogin(accountData, password, configJSON, subaccountData strin
return makeJSONResponse(err)
}
api.RunAsync(func() error {
log.Debug("starting a node, and saving account with configuration", "address", account.Address)
log.Debug("starting a node, and saving account with configuration", "key-uid", account.KeyUID)
err := statusBackend.StartNodeWithAccountAndConfig(account, password, &conf, subaccs)
if err != nil {
log.Error("failed to start node and save account", "address", account.Address, "error", err)
log.Error("failed to start node and save account", "key-uid", account.KeyUID, "error", err)
return err
}
log.Debug("started a node, and saved account", "address", account.Address)
log.Debug("started a node, and saved account", "key-uid", account.KeyUID)
return nil
})
return makeJSONResponse(nil)
@ -387,13 +387,13 @@ func SaveAccountAndLoginWithKeycard(accountData, password, configJSON, keyHex st
return makeJSONResponse(err)
}
api.RunAsync(func() error {
log.Debug("starting a node, and saving account with configuration", "address", account.Address)
log.Debug("starting a node, and saving account with configuration", "key-uid", account.KeyUID)
err := statusBackend.SaveAccountAndStartNodeWithKey(account, &conf, password, keyHex)
if err != nil {
log.Error("failed to start node and save account", "address", account.Address, "error", err)
log.Error("failed to start node and save account", "key-uid", account.KeyUID, "error", err)
return err
}
log.Debug("started a node, and saved account", "address", account.Address)
log.Debug("started a node, and saved account", "key-uid", account.KeyUID)
return nil
})
return makeJSONResponse(nil)
@ -408,13 +408,13 @@ func LoginWithKeycard(accountData, password, keyHex string) string {
return makeJSONResponse(err)
}
api.RunAsync(func() error {
log.Debug("start a node with account", "address", account.Address)
log.Debug("start a node with account", "key-uid", account.KeyUID)
err := statusBackend.StartNodeWithKey(account, password, keyHex)
if err != nil {
log.Error("failed to start a node", "address", account.Address, "error", err)
log.Error("failed to start a node", "key-uid", account.KeyUID, "error", err)
return err
}
log.Debug("started a node with", "address", account.Address)
log.Debug("started a node with", "key-uid", account.KeyUID)
return nil
})
return makeJSONResponse(nil)

View File

@ -3,19 +3,17 @@ package multiaccounts
import (
"database/sql"
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/multiaccounts/migrations"
"github.com/status-im/status-go/sqlite"
)
// Account stores public information about account.
type Account struct {
Name string `json:"name"`
Address common.Address `json:"address"`
Timestamp int64 `json:"timestamp"`
PhotoPath string `json:"photo-path"`
KeycardPairing string `json:"keycard-pairing"`
KeyUID string `json:"key-uid"`
Name string `json:"name"`
Timestamp int64 `json:"timestamp"`
PhotoPath string `json:"photo-path"`
KeycardPairing string `json:"keycard-pairing"`
KeyUID string `json:"key-uid"`
}
// InitializeDB creates db file at a given path and applies migrations.
@ -40,7 +38,7 @@ func (db *Database) Close() error {
}
func (db *Database) GetAccounts() ([]Account, error) {
rows, err := db.db.Query("SELECT address, name, loginTimestamp, photoPath, keycardPairing, keyUid from accounts ORDER BY loginTimestamp DESC")
rows, err := db.db.Query("SELECT name, loginTimestamp, photoPath, keycardPairing, keyUid from accounts ORDER BY loginTimestamp DESC")
if err != nil {
return nil, err
}
@ -48,7 +46,7 @@ func (db *Database) GetAccounts() ([]Account, error) {
inthelper := sql.NullInt64{}
for rows.Next() {
acc := Account{}
err = rows.Scan(&acc.Address, &acc.Name, &inthelper, &acc.PhotoPath, &acc.KeycardPairing, &acc.KeyUID)
err = rows.Scan(&acc.Name, &inthelper, &acc.PhotoPath, &acc.KeycardPairing, &acc.KeyUID)
if err != nil {
return nil, err
}
@ -59,21 +57,21 @@ func (db *Database) GetAccounts() ([]Account, error) {
}
func (db *Database) SaveAccount(account Account) error {
_, err := db.db.Exec("INSERT OR REPLACE INTO accounts (address, name, photoPath, keycardPairing, keyUid) VALUES (?, ?, ?, ?, ?)", account.Address, account.Name, account.PhotoPath, account.KeycardPairing, account.KeyUID)
_, err := db.db.Exec("INSERT OR REPLACE INTO accounts (name, photoPath, keycardPairing, keyUid) VALUES (?, ?, ?, ?)", account.Name, account.PhotoPath, account.KeycardPairing, account.KeyUID)
return err
}
func (db *Database) UpdateAccount(account Account) error {
_, err := db.db.Exec("UPDATE accounts SET name = ?, photoPath = ?, keycardPairing = ?, keyUid = ? WHERE address = ?", account.Name, account.PhotoPath, account.KeycardPairing, account.KeyUID, account.Address)
_, err := db.db.Exec("UPDATE accounts SET name = ?, photoPath = ?, keycardPairing = ? WHERE keyUid = ?", account.Name, account.PhotoPath, account.KeycardPairing, account.KeyUID)
return err
}
func (db *Database) UpdateAccountTimestamp(address common.Address, loginTimestamp int64) error {
_, err := db.db.Exec("UPDATE accounts SET loginTimestamp = ? WHERE address = ?", loginTimestamp, address)
func (db *Database) UpdateAccountTimestamp(keyUID string, loginTimestamp int64) error {
_, err := db.db.Exec("UPDATE accounts SET loginTimestamp = ? WHERE keyUid = ?", loginTimestamp, keyUID)
return err
}
func (db *Database) DeleteAccount(address common.Address) error {
_, err := db.db.Exec("DELETE FROM accounts WHERE address = ?", address)
func (db *Database) DeleteAccount(keyUID string) error {
_, err := db.db.Exec("DELETE FROM accounts WHERE keyUid = ?", keyUID)
return err
}

View File

@ -5,7 +5,6 @@ import (
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
@ -23,7 +22,7 @@ func setupTestDB(t *testing.T) (*Database, func()) {
func TestAccounts(t *testing.T) {
db, stop := setupTestDB(t)
defer stop()
expected := Account{Name: "string", Address: common.Address{0xff}}
expected := Account{Name: "string", KeyUID: "string"}
require.NoError(t, db.SaveAccount(expected))
accounts, err := db.GetAccounts()
require.NoError(t, err)
@ -34,7 +33,7 @@ func TestAccounts(t *testing.T) {
func TestAccountsUpdate(t *testing.T) {
db, stop := setupTestDB(t)
defer stop()
expected := Account{Address: common.Address{0x01}}
expected := Account{KeyUID: "string"}
require.NoError(t, db.SaveAccount(expected))
expected.PhotoPath = "chars"
require.NoError(t, db.UpdateAccount(expected))
@ -48,12 +47,12 @@ func TestLoginUpdate(t *testing.T) {
db, stop := setupTestDB(t)
defer stop()
accounts := []Account{{Name: "first", Address: common.Address{0xff}}, {Name: "second", Address: common.Address{0xf1}}}
accounts := []Account{{Name: "first", KeyUID: "0x1"}, {Name: "second", KeyUID: "0x2"}}
for _, acc := range accounts {
require.NoError(t, db.SaveAccount(acc))
}
require.NoError(t, db.UpdateAccountTimestamp(accounts[0].Address, 100))
require.NoError(t, db.UpdateAccountTimestamp(accounts[1].Address, 10))
require.NoError(t, db.UpdateAccountTimestamp(accounts[0].KeyUID, 100))
require.NoError(t, db.UpdateAccountTimestamp(accounts[1].KeyUID, 10))
accounts[0].Timestamp = 100
accounts[1].Timestamp = 10
rst, err := db.GetAccounts()

View File

@ -1,7 +1,7 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
// 0001_accounts.down.sql (21B)
// 0001_accounts.up.sql (177B)
// 0001_accounts.up.sql (163B)
// doc.go (74B)
package migrations
@ -86,12 +86,12 @@ func _0001_accountsDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "0001_accounts.down.sql", size: 21, mode: os.FileMode(0644), modTime: time.Unix(1573806410, 0)}
info := bindataFileInfo{name: "0001_accounts.down.sql", size: 21, mode: os.FileMode(0644), modTime: time.Unix(1573216280, 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
}
var __0001_accountsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x44\xcb\xb1\xae\x82\x30\x14\x80\xe1\xbd\x4f\x71\xc6\x7b\x93\xbe\x81\x53\xc1\x2a\x8d\x08\xa4\x1c\x04\xc6\x13\xda\x40\xa3\xb4\x84\xd6\xc1\xb7\x37\x61\x71\xfc\xff\xe4\xcb\xb5\x14\x28\x01\x45\x56\x4a\x50\x17\xa8\x6a\x04\x39\xa8\x16\x5b\xa0\x69\x0a\x6f\x9f\x22\xfc\x31\x32\x66\xb7\x31\xc2\x43\xe8\xbc\x10\x1a\x1a\xad\xee\x42\x8f\x70\x93\x23\x67\x9e\x56\x0b\x28\x07\x3c\x70\xd5\x95\x25\x67\xaf\x30\x3b\x8f\x6e\xb5\x31\xd1\xba\x41\xa6\xae\xa0\x2a\xe4\x6c\x5b\x42\x0a\x0d\xa5\xe5\x00\x9c\x3d\xed\x67\xa2\xdd\x34\xe4\x76\xe7\xe7\xdf\xec\x9c\x39\x82\xfd\x43\xaf\xb0\xa8\x3b\x04\x5d\xf7\xea\x7c\x62\xdf\x00\x00\x00\xff\xff\xf9\xc7\x34\x46\xb1\x00\x00\x00")
var __0001_accountsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x1c\xcc\xb1\x6e\x83\x30\x14\x46\xe1\xdd\x4f\xf1\x8f\xad\xe4\x37\xe8\x64\xa8\x5b\xae\x42\x00\x99\x4b\x80\xd1\x02\x04\x56\x82\x8d\xc0\x19\xf2\xf6\x51\x58\x8f\xf4\x9d\xd4\x68\xc5\x1a\xac\x92\x5c\x83\xfe\x50\x94\x0c\xdd\x51\xcd\x35\xec\x30\x84\xa7\x8f\x07\xbe\xc4\x7d\x7a\x35\x6e\xc4\x4d\x99\x34\x53\x06\x95\xa1\xab\x32\x3d\x2e\xba\x97\xc2\xdb\x75\x02\xeb\x8e\x4f\x5b\x34\x79\x2e\xc5\x23\xcc\xce\xb3\x5b\xa7\x23\xda\x75\x43\x42\xff\xa0\x82\xa5\xd8\x96\x10\x43\x65\xe3\x72\x02\xf9\xf9\x0e\x76\x1f\x2b\xeb\x76\xe7\xe7\x33\x8a\x6f\xb4\xc4\x59\xd9\x30\x4c\xd9\xd2\xef\x8f\x78\x07\x00\x00\xff\xff\xab\xcf\xa2\xbd\xa3\x00\x00\x00")
func _0001_accountsUpSqlBytes() ([]byte, error) {
return bindataRead(
@ -106,8 +106,8 @@ func _0001_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "0001_accounts.up.sql", size: 177, mode: os.FileMode(0644), modTime: time.Unix(1574772479, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0x24, 0xcf, 0x7a, 0x7b, 0xb0, 0xf0, 0x95, 0x3f, 0x45, 0x63, 0x9c, 0xe1, 0xca, 0x8f, 0xe4, 0x1a, 0x83, 0x8b, 0x19, 0x1f, 0x11, 0x98, 0xc4, 0x2b, 0xa5, 0x2f, 0x79, 0x96, 0xc2, 0x8f, 0x75}}
info := bindataFileInfo{name: "0001_accounts.up.sql", size: 163, mode: os.FileMode(0644), modTime: time.Unix(1575565817, 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
}
@ -126,7 +126,7 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1573806410, 0)}
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1573216280, 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
}

View File

@ -1,8 +1,7 @@
CREATE TABLE IF NOT EXISTS accounts (
address VARCHAR PRIMARY KEY,
keyUid VARCHAR PRIMARY KEY,
name TEXT NOT NULL,
loginTimestamp BIG INT,
photoPath TEXT,
keycardPairing TEXT,
keyUid TEXT
keycardPairing TEXT
) WITHOUT ROWID;

View File

@ -23,12 +23,5 @@ type MultiAccountsAPI struct {
}
func (api *MultiAccountsAPI) UpdateAccount(account multiaccounts.Account) error {
expected, err := api.manager.MainAccountAddress()
if err != nil {
return err
}
if account.Address != expected {
return ErrUpdatingWrongAccount
}
return api.db.UpdateAccount(account)
}

View File

@ -2,6 +2,7 @@ package devtests
import (
"crypto/ecdsa"
"crypto/sha256"
"io/ioutil"
"os"
@ -11,6 +12,7 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/params"
@ -62,9 +64,11 @@ func (s *DevNodeSuite) SetupTest() {
s.Require().NoError(err)
s.backend.UpdateRootDataDir(s.dir)
s.Require().NoError(s.backend.OpenAccounts())
keyUIDHex := sha256.Sum256(crypto.FromECDSAPub(&account.PublicKey))
keyUID := types.EncodeHex(keyUIDHex[:])
s.Require().NoError(s.backend.StartNodeWithAccountAndConfig(multiaccounts.Account{
Name: "main",
Address: s.DevAccountAddress,
Name: "main",
KeyUID: keyUID,
}, "test", config, []accounts.Account{{Address: s.DevAccountAddress, Wallet: true, Chat: true}}))
s.Remote, err = s.miner.Attach()
s.Require().NoError(err)

View File

@ -113,7 +113,7 @@ func (s *TransactionsTestSuite) TestEmptyToFieldPreserved() {
defer os.Remove(tmpdir)
wallet := common.HexToAddress(utils.TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{Address: wallet}, utils.TestConfig.Account1.Password,
s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithDataDir(tmpdir),
)
@ -186,7 +186,7 @@ func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc
defer os.Remove(tmpdir)
wallet := common.HexToAddress(utils.TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{Address: wallet}, utils.TestConfig.Account1.Password,
s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithDataDir(tmpdir),
)
@ -223,7 +223,7 @@ func (s *TransactionsTestSuite) TestSendEther() {
defer os.Remove(tmpdir)
wallet := common.HexToAddress(utils.TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{Address: wallet}, utils.TestConfig.Account1.Password,
s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithDataDir(tmpdir),
)
@ -250,7 +250,7 @@ func (s *TransactionsTestSuite) TestSendEtherTxUpstream() {
s.NoError(err)
wallet := common.HexToAddress(utils.TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{Address: wallet}, utils.TestConfig.Account1.Password,
s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithUpstream(addr),
e2e.WithDataDir(tmpdir),