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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
@ -10,17 +11,22 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
gethkeystore "github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"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/keystore"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"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
|
||||
|
@ -32,6 +38,7 @@ var (
|
|||
ErrOnboardingNotStarted = errors.New("onboarding must be started before choosing an account")
|
||||
ErrOnboardingAccountNotFound = errors.New("cannot find onboarding account with the given id")
|
||||
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 {
|
||||
|
@ -44,11 +51,24 @@ func (e ErrCannotLocateKeyFile) Error() string {
|
|||
|
||||
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.
|
||||
type Manager struct {
|
||||
mu sync.RWMutex
|
||||
Keydir string
|
||||
keystore types.KeyStore
|
||||
mu sync.RWMutex
|
||||
rpcClient *rpc.Client
|
||||
rpcTimeout time.Duration
|
||||
Keydir string
|
||||
keystore types.KeyStore
|
||||
|
||||
accountsGenerator *generator.Generator
|
||||
onboarding *Onboarding
|
||||
|
@ -612,3 +632,96 @@ func (m *Manager) ReEncryptKeyStoreDir(keyDirPath, oldPass, newPass string) erro
|
|||
func (m *Manager) DeleteAccount(address types.Address, password string) error {
|
||||
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
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
|
||||
"github.com/status-im/status-go/account/generator"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
)
|
||||
|
||||
// GethManager represents account manager interface.
|
||||
|
@ -20,6 +23,11 @@ func NewGethManager() *GethManager {
|
|||
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.
|
||||
func (m *GethManager) InitKeystore(keydir string) error {
|
||||
m.mu.Lock()
|
||||
|
|
|
@ -960,7 +960,7 @@ func (b *GethStatusBackend) startNode(config *params.NodeConfig) (err error) {
|
|||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.accountManager.SetRPCClient(b.statusNode.RPCClient(), rpc.DefaultCallTimeout)
|
||||
signal.SendNodeStarted()
|
||||
|
||||
b.transactor.SetNetworkID(config.NetworkID)
|
||||
|
@ -1456,7 +1456,7 @@ func (b *GethStatusBackend) injectAccountsIntoWakuService(w types.WakuKeyManager
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
// Set initial connection state
|
||||
|
|
|
@ -226,6 +226,7 @@ func main() {
|
|||
gethbridge.NewNodeBridge(backend.StatusNode().GethNode(), backend.StatusNode().WakuService(), backend.StatusNode().WakuV2Service()),
|
||||
installationID.String(),
|
||||
nil,
|
||||
backend.AccountManager(),
|
||||
options...,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -86,6 +86,7 @@ func (s *MessengerCommunitiesSuite) newMessengerWithOptions(shh types.Waku, priv
|
|||
&testNode{shh: shh},
|
||||
uuid.New().String(),
|
||||
nil,
|
||||
nil,
|
||||
options...,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"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/appmetrics"
|
||||
"github.com/status-im/status-go/connection"
|
||||
|
@ -106,6 +107,7 @@ type Messenger struct {
|
|||
pushNotificationClient *pushnotificationclient.Client
|
||||
pushNotificationServer *pushnotificationserver.Server
|
||||
communitiesManager *communities.Manager
|
||||
accountsManager *account.GethManager
|
||||
logger *zap.Logger
|
||||
|
||||
outputCSV bool
|
||||
|
@ -238,6 +240,7 @@ func NewMessenger(
|
|||
node types.Node,
|
||||
installationID string,
|
||||
peerStore *mailservers.PeerStore,
|
||||
accountsManager *account.GethManager,
|
||||
opts ...Option,
|
||||
) (*Messenger, error) {
|
||||
var messenger *Messenger
|
||||
|
@ -434,6 +437,7 @@ func NewMessenger(
|
|||
pushNotificationClient: pushNotificationClient,
|
||||
pushNotificationServer: pushNotificationServer,
|
||||
communitiesManager: communitiesManager,
|
||||
accountsManager: accountsManager,
|
||||
ensVerifier: ensVerifier,
|
||||
featureFlags: c.featureFlags,
|
||||
systemMessagesTranslations: c.systemMessagesTranslations,
|
||||
|
|
|
@ -100,6 +100,7 @@ func newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey, logger *z
|
|||
&testNode{shh: shh},
|
||||
uuid.New().String(),
|
||||
nil,
|
||||
nil,
|
||||
options...,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -113,6 +113,7 @@ func (s *MessengerSyncSettingsSuite) newMessengerWithOptions(shh types.Waku, pri
|
|||
&testNode{shh: shh},
|
||||
uuid.New().String(),
|
||||
nil,
|
||||
nil,
|
||||
options...,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
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/db"
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
if !s.config.ShhextConfig.PFSEnabled {
|
||||
return nil
|
||||
|
@ -157,6 +158,7 @@ func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, db *
|
|||
s.n,
|
||||
s.config.ShhextConfig.InstallationID,
|
||||
s.peerStore,
|
||||
accountManager,
|
||||
options...,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -137,7 +137,7 @@ func TestInitProtocol(t *testing.T) {
|
|||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ func (s *ShhExtSuite) createAndAddNode() {
|
|||
|
||||
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)
|
||||
|
||||
stack.RegisterLifecycle(service)
|
||||
|
|
Loading…
Reference in New Issue