diff --git a/VERSION b/VERSION index 19199bcca..d647c8a44 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.36.1 +0.36.2 diff --git a/api/backend_test.go b/api/backend_test.go index f65706b31..be58866fa 100644 --- a/api/backend_test.go +++ b/api/backend_test.go @@ -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) diff --git a/api/geth_backend.go b/api/geth_backend.go index 385027ec2..6a6c4d36f 100644 --- a/api/geth_backend.go +++ b/api/geth_backend.go @@ -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 } diff --git a/lib/library_test_utils.go b/lib/library_test_utils.go index d2d358d80..78f57b528 100644 --- a/lib/library_test_utils.go +++ b/lib/library_test_utils.go @@ -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)) } diff --git a/mobile/status.go b/mobile/status.go index 0e924ea7f..e6455d64c 100644 --- a/mobile/status.go +++ b/mobile/status.go @@ -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) diff --git a/multiaccounts/database.go b/multiaccounts/database.go index bb2d0dbae..f1a077674 100644 --- a/multiaccounts/database.go +++ b/multiaccounts/database.go @@ -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 } diff --git a/multiaccounts/database_test.go b/multiaccounts/database_test.go index 724ab85c7..b08206e3c 100644 --- a/multiaccounts/database_test.go +++ b/multiaccounts/database_test.go @@ -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() diff --git a/multiaccounts/migrations/bindata.go b/multiaccounts/migrations/bindata.go index 22b87d432..0bf541d19 100644 --- a/multiaccounts/migrations/bindata.go +++ b/multiaccounts/migrations/bindata.go @@ -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 } diff --git a/multiaccounts/migrations/sql/0001_accounts.up.sql b/multiaccounts/migrations/sql/0001_accounts.up.sql index b745ded3f..f156b8d5c 100644 --- a/multiaccounts/migrations/sql/0001_accounts.up.sql +++ b/multiaccounts/migrations/sql/0001_accounts.up.sql @@ -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; diff --git a/services/accounts/multiaccounts.go b/services/accounts/multiaccounts.go index eda42f92a..78c6789f7 100644 --- a/services/accounts/multiaccounts.go +++ b/services/accounts/multiaccounts.go @@ -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) } diff --git a/t/devtests/devnode.go b/t/devtests/devnode.go index 758462a02..284ad3bb1 100644 --- a/t/devtests/devnode.go +++ b/t/devtests/devnode.go @@ -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) diff --git a/t/e2e/transactions/transactions_test.go b/t/e2e/transactions/transactions_test.go index f441bd662..c927cfe01 100644 --- a/t/e2e/transactions/transactions_test.go +++ b/t/e2e/transactions/transactions_test.go @@ -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),