Add methods to sign and recover messages/signatures to AccountManager
Also, make AccountManager a dependency of Messenger. This is needed for community token permissions as we'll need a way to access wallet accounts and sign messages when sending requests to join a community. The APIs have been mostly taken from GethStatusBackend and personal service.
This commit is contained in:
parent
612efb9b89
commit
6859a1d3b7
|
@ -1,6 +1,7 @@
|
||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -10,17 +11,22 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
gethkeystore "github.com/ethereum/go-ethereum/accounts/keystore"
|
gethkeystore "github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/status-im/status-go/account/generator"
|
"github.com/status-im/status-go/account/generator"
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/eth-node/keystore"
|
"github.com/status-im/status-go/eth-node/keystore"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/extkeys"
|
"github.com/status-im/status-go/extkeys"
|
||||||
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||||
|
"github.com/status-im/status-go/params"
|
||||||
|
"github.com/status-im/status-go/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
|
@ -32,6 +38,7 @@ var (
|
||||||
ErrOnboardingNotStarted = errors.New("onboarding must be started before choosing an account")
|
ErrOnboardingNotStarted = errors.New("onboarding must be started before choosing an account")
|
||||||
ErrOnboardingAccountNotFound = errors.New("cannot find onboarding account with the given id")
|
ErrOnboardingAccountNotFound = errors.New("cannot find onboarding account with the given id")
|
||||||
ErrAccountKeyStoreMissing = errors.New("account key store is not set")
|
ErrAccountKeyStoreMissing = errors.New("account key store is not set")
|
||||||
|
ErrInvalidPersonalSignAccount = errors.New("invalid account as only the selected one can generate a signature")
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrCannotLocateKeyFile struct {
|
type ErrCannotLocateKeyFile struct {
|
||||||
|
@ -44,9 +51,22 @@ func (e ErrCannotLocateKeyFile) Error() string {
|
||||||
|
|
||||||
var zeroAddress = types.Address{}
|
var zeroAddress = types.Address{}
|
||||||
|
|
||||||
|
type SignParams struct {
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
Address string `json:"account"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecoverParams struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
// Manager represents account manager interface.
|
// Manager represents account manager interface.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
rpcClient *rpc.Client
|
||||||
|
rpcTimeout time.Duration
|
||||||
Keydir string
|
Keydir string
|
||||||
keystore types.KeyStore
|
keystore types.KeyStore
|
||||||
|
|
||||||
|
@ -612,3 +632,96 @@ func (m *Manager) ReEncryptKeyStoreDir(keyDirPath, oldPass, newPass string) erro
|
||||||
func (m *Manager) DeleteAccount(address types.Address, password string) error {
|
func (m *Manager) DeleteAccount(address types.Address, password string) error {
|
||||||
return m.keystore.Delete(types.Account{Address: address}, password)
|
return m.keystore.Delete(types.Account{Address: address}, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*SelectedExtKey, error) {
|
||||||
|
exists, err := db.AddressExists(types.HexToAddress(address))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.New("account doesn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := m.VerifyAccountPassword(m.Keydir, address, password)
|
||||||
|
if _, ok := err.(*ErrCannotLocateKeyFile); ok {
|
||||||
|
key, err = m.generatePartialAccountKey(db, address, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SelectedExtKey{
|
||||||
|
Address: key.Address,
|
||||||
|
AccountKey: key,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) generatePartialAccountKey(db *accounts.Database, address string, password string) (*types.Key, error) {
|
||||||
|
dbPath, err := db.GetPath(types.HexToAddress(address))
|
||||||
|
path := "m/" + dbPath[strings.LastIndex(dbPath, "/")+1:]
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rootAddress, err := db.GetWalletRootAddress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
info, err := m.AccountsGenerator().LoadAccount(rootAddress.Hex(), password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
masterID := info.ID
|
||||||
|
|
||||||
|
accInfosMap, err := m.AccountsGenerator().StoreDerivedAccounts(masterID, password, []string{path})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, key, err := m.AddressToDecryptedAccount(accInfosMap[path].Address, password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Recover(rpcParams RecoverParams) (addr types.Address, err error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), m.rpcTimeout)
|
||||||
|
defer cancel()
|
||||||
|
var gethAddr gethcommon.Address
|
||||||
|
err = m.rpcClient.CallContextIgnoringLocalHandlers(
|
||||||
|
ctx,
|
||||||
|
&gethAddr,
|
||||||
|
m.rpcClient.UpstreamChainID,
|
||||||
|
params.PersonalRecoverMethodName,
|
||||||
|
rpcParams.Message, rpcParams.Signature)
|
||||||
|
addr = types.Address(gethAddr)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Sign(rpcParams SignParams, verifiedAccount *SelectedExtKey) (result types.HexBytes, err error) {
|
||||||
|
if !strings.EqualFold(rpcParams.Address, verifiedAccount.Address.Hex()) {
|
||||||
|
err = ErrInvalidPersonalSignAccount
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), m.rpcTimeout)
|
||||||
|
defer cancel()
|
||||||
|
var gethResult hexutil.Bytes
|
||||||
|
err = m.rpcClient.CallContextIgnoringLocalHandlers(
|
||||||
|
ctx,
|
||||||
|
&gethResult,
|
||||||
|
m.rpcClient.UpstreamChainID,
|
||||||
|
params.PersonalSignMethodName,
|
||||||
|
rpcParams.Data, rpcParams.Address, rpcParams.Password)
|
||||||
|
result = types.HexBytes(gethResult)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
|
||||||
"github.com/status-im/status-go/account/generator"
|
"github.com/status-im/status-go/account/generator"
|
||||||
|
"github.com/status-im/status-go/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GethManager represents account manager interface.
|
// GethManager represents account manager interface.
|
||||||
|
@ -20,6 +23,11 @@ func NewGethManager() *GethManager {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *GethManager) SetRPCClient(rpcClient *rpc.Client, rpcTimeout time.Duration) {
|
||||||
|
m.Manager.rpcClient = rpcClient
|
||||||
|
m.Manager.rpcTimeout = rpcTimeout
|
||||||
|
}
|
||||||
|
|
||||||
// InitKeystore sets key manager and key store.
|
// InitKeystore sets key manager and key store.
|
||||||
func (m *GethManager) InitKeystore(keydir string) error {
|
func (m *GethManager) InitKeystore(keydir string) error {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
|
|
|
@ -960,7 +960,7 @@ func (b *GethStatusBackend) startNode(config *params.NodeConfig) (err error) {
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
b.accountManager.SetRPCClient(b.statusNode.RPCClient(), rpc.DefaultCallTimeout)
|
||||||
signal.SendNodeStarted()
|
signal.SendNodeStarted()
|
||||||
|
|
||||||
b.transactor.SetNetworkID(config.NetworkID)
|
b.transactor.SetNetworkID(config.NetworkID)
|
||||||
|
@ -1456,7 +1456,7 @@ func (b *GethStatusBackend) injectAccountsIntoWakuService(w types.WakuKeyManager
|
||||||
}
|
}
|
||||||
|
|
||||||
if st != nil {
|
if st != nil {
|
||||||
if err := st.InitProtocol(b.statusNode.GethNode().Config().Name, identity, b.appDB, b.statusNode.HTTPServer(), b.multiaccountsDB, acc, logutils.ZapLogger()); err != nil {
|
if err := st.InitProtocol(b.statusNode.GethNode().Config().Name, identity, b.appDB, b.statusNode.HTTPServer(), b.multiaccountsDB, acc, b.accountManager, logutils.ZapLogger()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Set initial connection state
|
// Set initial connection state
|
||||||
|
|
|
@ -226,6 +226,7 @@ func main() {
|
||||||
gethbridge.NewNodeBridge(backend.StatusNode().GethNode(), backend.StatusNode().WakuService(), backend.StatusNode().WakuV2Service()),
|
gethbridge.NewNodeBridge(backend.StatusNode().GethNode(), backend.StatusNode().WakuService(), backend.StatusNode().WakuV2Service()),
|
||||||
installationID.String(),
|
installationID.String(),
|
||||||
nil,
|
nil,
|
||||||
|
backend.AccountManager(),
|
||||||
options...,
|
options...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -86,6 +86,7 @@ func (s *MessengerCommunitiesSuite) newMessengerWithOptions(shh types.Waku, priv
|
||||||
&testNode{shh: shh},
|
&testNode{shh: shh},
|
||||||
uuid.New().String(),
|
uuid.New().String(),
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
options...,
|
options...,
|
||||||
)
|
)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/status-im/status-go/account"
|
||||||
"github.com/status-im/status-go/appdatabase"
|
"github.com/status-im/status-go/appdatabase"
|
||||||
"github.com/status-im/status-go/appmetrics"
|
"github.com/status-im/status-go/appmetrics"
|
||||||
"github.com/status-im/status-go/connection"
|
"github.com/status-im/status-go/connection"
|
||||||
|
@ -106,6 +107,7 @@ type Messenger struct {
|
||||||
pushNotificationClient *pushnotificationclient.Client
|
pushNotificationClient *pushnotificationclient.Client
|
||||||
pushNotificationServer *pushnotificationserver.Server
|
pushNotificationServer *pushnotificationserver.Server
|
||||||
communitiesManager *communities.Manager
|
communitiesManager *communities.Manager
|
||||||
|
accountsManager *account.GethManager
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
|
|
||||||
outputCSV bool
|
outputCSV bool
|
||||||
|
@ -238,6 +240,7 @@ func NewMessenger(
|
||||||
node types.Node,
|
node types.Node,
|
||||||
installationID string,
|
installationID string,
|
||||||
peerStore *mailservers.PeerStore,
|
peerStore *mailservers.PeerStore,
|
||||||
|
accountsManager *account.GethManager,
|
||||||
opts ...Option,
|
opts ...Option,
|
||||||
) (*Messenger, error) {
|
) (*Messenger, error) {
|
||||||
var messenger *Messenger
|
var messenger *Messenger
|
||||||
|
@ -434,6 +437,7 @@ func NewMessenger(
|
||||||
pushNotificationClient: pushNotificationClient,
|
pushNotificationClient: pushNotificationClient,
|
||||||
pushNotificationServer: pushNotificationServer,
|
pushNotificationServer: pushNotificationServer,
|
||||||
communitiesManager: communitiesManager,
|
communitiesManager: communitiesManager,
|
||||||
|
accountsManager: accountsManager,
|
||||||
ensVerifier: ensVerifier,
|
ensVerifier: ensVerifier,
|
||||||
featureFlags: c.featureFlags,
|
featureFlags: c.featureFlags,
|
||||||
systemMessagesTranslations: c.systemMessagesTranslations,
|
systemMessagesTranslations: c.systemMessagesTranslations,
|
||||||
|
|
|
@ -100,6 +100,7 @@ func newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey, logger *z
|
||||||
&testNode{shh: shh},
|
&testNode{shh: shh},
|
||||||
uuid.New().String(),
|
uuid.New().String(),
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
options...,
|
options...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -113,6 +113,7 @@ func (s *MessengerSyncSettingsSuite) newMessengerWithOptions(shh types.Waku, pri
|
||||||
&testNode{shh: shh},
|
&testNode{shh: shh},
|
||||||
uuid.New().String(),
|
uuid.New().String(),
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
options...,
|
options...,
|
||||||
)
|
)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/account"
|
||||||
"github.com/status-im/status-go/connection"
|
"github.com/status-im/status-go/connection"
|
||||||
"github.com/status-im/status-go/db"
|
"github.com/status-im/status-go/db"
|
||||||
coretypes "github.com/status-im/status-go/eth-node/core/types"
|
coretypes "github.com/status-im/status-go/eth-node/core/types"
|
||||||
|
@ -107,7 +108,7 @@ func (s *Service) GetPeer(rawURL string) (*enode.Node, error) {
|
||||||
return enode.ParseV4(rawURL)
|
return enode.ParseV4(rawURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, db *sql.DB, httpServer *server.MediaServer, multiAccountDb *multiaccounts.Database, acc *multiaccounts.Account, logger *zap.Logger) error {
|
func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, db *sql.DB, httpServer *server.MediaServer, multiAccountDb *multiaccounts.Database, acc *multiaccounts.Account, accountManager *account.GethManager, logger *zap.Logger) error {
|
||||||
var err error
|
var err error
|
||||||
if !s.config.ShhextConfig.PFSEnabled {
|
if !s.config.ShhextConfig.PFSEnabled {
|
||||||
return nil
|
return nil
|
||||||
|
@ -157,6 +158,7 @@ func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, db *
|
||||||
s.n,
|
s.n,
|
||||||
s.config.ShhextConfig.InstallationID,
|
s.config.ShhextConfig.InstallationID,
|
||||||
s.peerStore,
|
s.peerStore,
|
||||||
|
accountManager,
|
||||||
options...,
|
options...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -137,7 +137,7 @@ func TestInitProtocol(t *testing.T) {
|
||||||
|
|
||||||
acc := &multiaccounts.Account{KeyUID: "0xdeadbeef"}
|
acc := &multiaccounts.Account{KeyUID: "0xdeadbeef"}
|
||||||
|
|
||||||
err = service.InitProtocol("Test", privateKey, sqlDB, nil, multiAccounts, acc, zap.NewNop())
|
err = service.InitProtocol("Test", privateKey, sqlDB, nil, multiAccounts, acc, nil, zap.NewNop())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ func (s *ShhExtSuite) createAndAddNode() {
|
||||||
|
|
||||||
acc := &multiaccounts.Account{KeyUID: "0xdeadbeef"}
|
acc := &multiaccounts.Account{KeyUID: "0xdeadbeef"}
|
||||||
|
|
||||||
err = service.InitProtocol("Test", privateKey, sqlDB, nil, multiAccounts, acc, zap.NewNop())
|
err = service.InitProtocol("Test", privateKey, sqlDB, nil, multiAccounts, acc, nil, zap.NewNop())
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
|
|
||||||
stack.RegisterLifecycle(service)
|
stack.RegisterLifecycle(service)
|
||||||
|
|
Loading…
Reference in New Issue