github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/identity/manager.go (about)

     1  /*
     2   * Copyright (C) 2017 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  // Maps Ethereum account to dto.Identity.
    19  // Currently creates a new eth account with passphrase on CreateNewIdentity().
    20  
    21  package identity
    22  
    23  import (
    24  	"sync"
    25  
    26  	"github.com/ethereum/go-ethereum/accounts"
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/pkg/errors"
    29  	"github.com/rs/zerolog/log"
    30  
    31  	"github.com/mysteriumnetwork/node/eventbus"
    32  )
    33  
    34  // Identity events
    35  const (
    36  	AppTopicIdentityUnlock  = "identity-unlocked"
    37  	AppTopicIdentityCreated = "identity-created"
    38  )
    39  
    40  // AppEventIdentityUnlock represents the payload that is sent on identity unlock.
    41  type AppEventIdentityUnlock struct {
    42  	ChainID int64
    43  	ID      Identity
    44  }
    45  
    46  // ResidentCountryEvent represent actual resident country changed event
    47  type ResidentCountryEvent struct {
    48  	ID      string
    49  	Country string
    50  }
    51  
    52  type identityManager struct {
    53  	keystoreManager keystore
    54  	residentCountry *ResidentCountry
    55  	unlocked        map[string]bool // Currently unlocked addresses
    56  	unlockedMu      sync.RWMutex
    57  	eventBus        eventbus.EventBus
    58  }
    59  
    60  // keystore allows actions with accounts (listing, creating, unlocking, signing)
    61  type keystore interface {
    62  	Accounts() []accounts.Account
    63  	NewAccount(passphrase string) (accounts.Account, error)
    64  	Find(a accounts.Account) (accounts.Account, error)
    65  	Unlock(a accounts.Account, passphrase string) error
    66  	SignHash(a accounts.Account, hash []byte) ([]byte, error)
    67  }
    68  
    69  // NewIdentityManager creates and returns new identityManager
    70  func NewIdentityManager(keystore keystore, eventBus eventbus.EventBus, residentCountry *ResidentCountry) *identityManager {
    71  	return &identityManager{
    72  		keystoreManager: keystore,
    73  		residentCountry: residentCountry,
    74  		unlocked:        map[string]bool{},
    75  		eventBus:        eventBus,
    76  	}
    77  }
    78  
    79  // GetUnlockedIdentity retrieves unlocked identity
    80  func (idm *identityManager) GetUnlockedIdentity() (Identity, bool) {
    81  	for _, identity := range idm.GetIdentities() {
    82  		if idm.IsUnlocked(identity.Address) {
    83  			return identity, true
    84  		}
    85  	}
    86  	return Identity{}, false
    87  }
    88  
    89  // IsUnlocked checks if the given identity is unlocked or not
    90  func (idm *identityManager) IsUnlocked(identity string) bool {
    91  	idm.unlockedMu.Lock()
    92  	defer idm.unlockedMu.Unlock()
    93  	_, ok := idm.unlocked[identity]
    94  	return ok
    95  }
    96  
    97  func accountToIdentity(account accounts.Account) Identity {
    98  	identity := FromAddress(account.Address.Hex())
    99  	return identity
   100  }
   101  
   102  func identityToAccount(identity Identity) accounts.Account {
   103  	return addressToAccount(identity.Address)
   104  }
   105  
   106  func addressToAccount(address string) accounts.Account {
   107  	return accounts.Account{
   108  		Address: common.HexToAddress(address),
   109  	}
   110  }
   111  
   112  func (idm *identityManager) CreateNewIdentity(passphrase string) (identity Identity, err error) {
   113  	account, err := idm.keystoreManager.NewAccount(passphrase)
   114  	if err != nil {
   115  		return identity, err
   116  	}
   117  
   118  	identity = accountToIdentity(account)
   119  	idm.eventBus.Publish(AppTopicIdentityCreated, identity.Address)
   120  	return identity, nil
   121  }
   122  
   123  func (idm *identityManager) GetIdentities() []Identity {
   124  	accountList := idm.keystoreManager.Accounts()
   125  
   126  	var ids = make([]Identity, len(accountList))
   127  	for i, account := range accountList {
   128  		ids[i] = accountToIdentity(account)
   129  	}
   130  
   131  	return ids
   132  }
   133  
   134  func (idm *identityManager) GetIdentity(address string) (identity Identity, err error) {
   135  	account, err := idm.findAccount(address)
   136  	if err != nil {
   137  		return identity, errors.New("identity not found")
   138  	}
   139  
   140  	return accountToIdentity(account), nil
   141  }
   142  
   143  func (idm *identityManager) HasIdentity(address string) bool {
   144  	_, err := idm.findAccount(address)
   145  	return err == nil
   146  }
   147  
   148  func (idm *identityManager) Unlock(chainID int64, address string, passphrase string) error {
   149  	idm.unlockedMu.Lock()
   150  	defer idm.unlockedMu.Unlock()
   151  
   152  	if idm.unlocked[address] {
   153  		log.Debug().Msg("Unlocked identity found in cache, skipping keystore: " + address)
   154  		return nil
   155  	}
   156  
   157  	account, err := idm.findAccount(address)
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	err = idm.keystoreManager.Unlock(account, passphrase)
   163  	if err != nil {
   164  		return errors.Wrapf(err, "keystore failed to unlock identity: %s", address)
   165  	}
   166  	log.Debug().Msgf("Caching unlocked address: %s", address)
   167  	idm.unlocked[address] = true
   168  
   169  	go func() {
   170  		idm.eventBus.Publish(AppTopicIdentityUnlock, AppEventIdentityUnlock{
   171  			ChainID: chainID,
   172  			ID:      FromAddress(address),
   173  		})
   174  		idm.residentCountry.publishResidentCountry(address)
   175  	}()
   176  
   177  	return nil
   178  }
   179  
   180  func (idm *identityManager) findAccount(address string) (accounts.Account, error) {
   181  	account, err := idm.keystoreManager.Find(addressToAccount(address))
   182  	if err != nil {
   183  		return accounts.Account{}, errors.New("identity not found: " + address)
   184  	}
   185  
   186  	return account, err
   187  }