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

     1  /*
     2   * Copyright (C) 2018 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  package registry
    19  
    20  import (
    21  	"fmt"
    22  	"sync"
    23  	"time"
    24  
    25  	"math/big"
    26  
    27  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/mysteriumnetwork/node/core/node/event"
    30  	"github.com/mysteriumnetwork/node/eventbus"
    31  	"github.com/mysteriumnetwork/node/identity"
    32  	identity_selector "github.com/mysteriumnetwork/node/identity/selector"
    33  	"github.com/mysteriumnetwork/payments/bindings"
    34  	paymentClient "github.com/mysteriumnetwork/payments/client"
    35  	"github.com/mysteriumnetwork/payments/crypto"
    36  	"github.com/pkg/errors"
    37  	"github.com/rs/zerolog/log"
    38  )
    39  
    40  type registryStorage interface {
    41  	Store(status StoredRegistrationStatus) error
    42  	Get(chainID int64, identity identity.Identity) (StoredRegistrationStatus, error)
    43  	GetAll() ([]StoredRegistrationStatus, error)
    44  }
    45  
    46  type hermesCaller interface {
    47  	IsIdentityOffchain(chainID int64, id string) (bool, error)
    48  }
    49  
    50  type transactor interface {
    51  	FetchRegistrationStatus(id string) ([]TransactorStatusResponse, error)
    52  	GetFreeProviderRegistrationEligibility() (bool, error)
    53  	RegisterProviderIdentity(id string, stake, fee *big.Int, beneficiary string, chainID int64, referralToken *string) error
    54  }
    55  
    56  type contractRegistry struct {
    57  	storage    registryStorage
    58  	stop       chan struct{}
    59  	once       sync.Once
    60  	publisher  eventbus.Publisher
    61  	lock       sync.Mutex
    62  	ethC       paymentClient.EtherClient
    63  	ap         AddressProvider
    64  	hermes     hermesCaller
    65  	transactor transactor
    66  	manager    identity.Manager
    67  	cfg        IdentityRegistryConfig
    68  }
    69  
    70  // IdentityRegistryConfig contains the configuration for registry contract.
    71  type IdentityRegistryConfig struct {
    72  	TransactorPollInterval time.Duration
    73  	TransactorPollTimeout  time.Duration
    74  }
    75  
    76  // NewIdentityRegistryContract creates identity registry service which uses blockchain for information
    77  func NewIdentityRegistryContract(ethClient paymentClient.EtherClient, ap AddressProvider, registryStorage registryStorage, publisher eventbus.Publisher, caller hermesCaller, transactor transactor, selector identity_selector.Handler, cfg IdentityRegistryConfig) (*contractRegistry, error) {
    78  	return &contractRegistry{
    79  		storage:    registryStorage,
    80  		stop:       make(chan struct{}),
    81  		publisher:  publisher,
    82  		ethC:       ethClient,
    83  		ap:         ap,
    84  		hermes:     caller,
    85  		transactor: transactor,
    86  		cfg:        cfg,
    87  	}, nil
    88  }
    89  
    90  // Subscribe subscribes the contract registry to relevant events
    91  func (registry *contractRegistry) Subscribe(eb eventbus.Subscriber) error {
    92  	err := eb.SubscribeAsync(event.AppTopicNode, registry.handleNodeEvent)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	err = eb.SubscribeAsync(AppTopicEthereumClientReconnected, registry.handleEtherClientReconnect)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	return eb.Subscribe(AppTopicTransactorRegistration, registry.handleRegistrationEvent)
   101  }
   102  
   103  // GetRegistrationStatus returns the registration status of the provided identity
   104  func (registry *contractRegistry) GetRegistrationStatus(chainID int64, id identity.Identity) (RegistrationStatus, error) {
   105  	var currentStatus RegistrationStatus
   106  	ss, err := registry.storage.Get(chainID, id)
   107  	switch err {
   108  	case nil:
   109  		currentStatus = ss.RegistrationStatus
   110  	case ErrNotFound:
   111  		currentStatus = Unregistered
   112  	default:
   113  		return Unregistered, errors.Wrap(err, "could not check status in local db")
   114  	}
   115  
   116  	if currentStatus == Registered {
   117  		return currentStatus, nil
   118  	}
   119  
   120  	var newStatus RegistrationStatus
   121  	newStatus, err = registry.bcRegistrationStatus(chainID, id)
   122  	if err != nil {
   123  		return Unregistered, errors.Wrap(err, "could not check identity registration status on blockchain")
   124  	}
   125  
   126  	if newStatus == Unregistered && ss.RegistrationStatus == InProgress {
   127  		newStatus = InProgress
   128  	}
   129  
   130  	if newStatus == Unregistered || newStatus == InProgress {
   131  		ok, err := registry.hermes.IsIdentityOffchain(chainID, id.Address)
   132  		if err != nil {
   133  			log.Err(err).Str("status", newStatus.String()).Msg("failed to contact hermes to get new registration status")
   134  		}
   135  
   136  		if ok && err == nil {
   137  			log.Debug().Str("identity", id.Address).Msg("identity is offchain, considering it registered")
   138  			newStatus = Registered
   139  		}
   140  	}
   141  
   142  	err = registry.storage.Store(StoredRegistrationStatus{
   143  		Identity:           id,
   144  		RegistrationStatus: newStatus,
   145  		ChainID:            chainID,
   146  	})
   147  	if err != nil {
   148  		return newStatus, errors.Wrap(err, "could not store registration status")
   149  	}
   150  
   151  	// If current status was not registered and we are now registered
   152  	// publish an event for that to make sure that wasn't missed.
   153  	if currentStatus != newStatus && newStatus == Registered {
   154  		go registry.publisher.Publish(AppTopicIdentityRegistration, AppEventIdentityRegistration{
   155  			ID:      id,
   156  			Status:  newStatus,
   157  			ChainID: chainID,
   158  		})
   159  	}
   160  	return newStatus, nil
   161  }
   162  
   163  func (registry *contractRegistry) handleNodeEvent(ev event.Payload) {
   164  	if ev.Status == event.StatusStarted {
   165  		registry.handleStart()
   166  		return
   167  	}
   168  	if ev.Status == event.StatusStopped {
   169  		registry.handleStop()
   170  		return
   171  	}
   172  }
   173  
   174  func (registry *contractRegistry) handleRegistrationEvent(ev IdentityRegistrationRequest) {
   175  	registry.lock.Lock()
   176  	defer registry.lock.Unlock()
   177  
   178  	status, err := registry.storage.Get(ev.ChainID, identity.FromAddress(ev.Identity))
   179  	if err != nil && err != ErrNotFound {
   180  		log.Error().Err(err).Msg("Could not get status from local db")
   181  		return
   182  	}
   183  
   184  	if err == ErrNotFound {
   185  		status = StoredRegistrationStatus{}.FromEvent(Unregistered, ev)
   186  		err := registry.storage.Store(status)
   187  		if err != nil {
   188  			log.Error().Err(err).Stack().Msg("Could not store registration status")
   189  			return
   190  		}
   191  	}
   192  
   193  	if status.RegistrationStatus == Registered {
   194  		log.Info().Msgf("Identity %q already registered, skipping", ev.Identity)
   195  		return
   196  	}
   197  
   198  	s := InProgress
   199  
   200  	// In case we have a previous registration, force re-check the BC status
   201  	if status.RegistrationStatus == InProgress || status.RegistrationStatus == RegistrationError {
   202  		status, err := registry.GetRegistrationStatus(ev.ChainID, identity.FromAddress(ev.Identity))
   203  		if err != nil {
   204  			log.Info().Err(err).Msg("could not recheck status with bc")
   205  		} else if status.Registered() {
   206  			s = Registered
   207  		}
   208  	}
   209  
   210  	ID := identity.FromAddress(ev.Identity)
   211  
   212  	go registry.publisher.Publish(AppTopicIdentityRegistration, AppEventIdentityRegistration{
   213  		ID:      ID,
   214  		Status:  s,
   215  		ChainID: ev.ChainID,
   216  	})
   217  	err = registry.storage.Store(StoredRegistrationStatus{
   218  		Identity:           ID,
   219  		RegistrationStatus: s,
   220  		ChainID:            ev.ChainID,
   221  	})
   222  	if err != nil {
   223  		log.Error().Err(err).Stack().Msg("Could not store registration status")
   224  	}
   225  
   226  	go registry.subscribeToRegistrationEventViaTransactor(ev)
   227  }
   228  
   229  func (registry *contractRegistry) subscribeToRegistrationEventViaTransactor(ev IdentityRegistrationRequest) {
   230  	timeout := time.After(registry.cfg.TransactorPollTimeout)
   231  	for {
   232  		select {
   233  		case <-registry.stop:
   234  			registry.saveRegistrationStatus(ev.ChainID, ev.Identity, RegistrationError)
   235  			return
   236  		case <-timeout:
   237  			log.Info().Msg("registration watch subscription timed out")
   238  			registry.saveRegistrationStatus(ev.ChainID, ev.Identity, RegistrationError)
   239  			return
   240  		case <-time.After(registry.cfg.TransactorPollInterval):
   241  			res, err := registry.transactor.FetchRegistrationStatus(ev.Identity)
   242  			if err != nil {
   243  				log.Warn().Err(err).Msg("could not fetch registration status from transactor")
   244  				break
   245  			}
   246  
   247  			var resp *TransactorStatusResponse
   248  			for _, v := range res {
   249  				if v.ChainID != ev.ChainID {
   250  					continue
   251  				}
   252  				resp = &v
   253  				break
   254  			}
   255  
   256  			if resp == nil {
   257  				log.Warn().Msg("no matching registration entries for chain in transactor response, will continue")
   258  				break
   259  			}
   260  
   261  			switch resp.Status {
   262  			case TransactorRegistrationEntryStatusSucceed:
   263  				registry.resyncWithBC(ev.ChainID, ev.Identity)
   264  				return
   265  			case TransactorRegistrationEntryStatusFailed:
   266  				log.Error().Msg("registration reported as failed by transactor, will check in bc just in case")
   267  				bcStatus, err := registry.bcRegistrationStatus(ev.ChainID, identity.FromAddress(ev.Identity))
   268  				if err != nil {
   269  					log.Err(err).Msg("got registration failed from transactor and failed to check registration status")
   270  					registry.saveRegistrationStatus(ev.ChainID, ev.Identity, RegistrationError)
   271  					return
   272  				}
   273  
   274  				statusToWrite := Registered
   275  				if bcStatus != Registered {
   276  					statusToWrite = RegistrationError
   277  				}
   278  
   279  				registry.saveRegistrationStatus(ev.ChainID, ev.Identity, statusToWrite)
   280  				return
   281  			}
   282  		}
   283  	}
   284  }
   285  
   286  func (registry *contractRegistry) resyncWithBC(chainID int64, id string) {
   287  	status, err := registry.bcRegistrationStatus(chainID, identity.FromAddress(id))
   288  	if err != nil {
   289  		log.Err(err).Msg("could not check registration status on chain")
   290  		registry.saveRegistrationStatus(chainID, id, RegistrationError)
   291  		return
   292  	}
   293  	registry.saveRegistrationStatus(chainID, id, status)
   294  }
   295  
   296  func (registry *contractRegistry) saveRegistrationStatus(chainID int64, id string, status RegistrationStatus) {
   297  	err := registry.storage.Store(StoredRegistrationStatus{
   298  		Identity:           identity.FromAddress(id),
   299  		RegistrationStatus: status,
   300  		ChainID:            chainID,
   301  	})
   302  	if err != nil {
   303  		log.Error().Err(err).Msg("Could not store registration status")
   304  	}
   305  
   306  	registry.publisher.Publish(AppTopicIdentityRegistration, AppEventIdentityRegistration{
   307  		ID:      identity.FromAddress(id),
   308  		Status:  status,
   309  		ChainID: chainID,
   310  	})
   311  }
   312  
   313  func (registry *contractRegistry) handleStop() {
   314  	registry.once.Do(func() {
   315  		log.Info().Msg("Stopping registry...")
   316  		close(registry.stop)
   317  	})
   318  }
   319  
   320  func (registry *contractRegistry) handleStart() {
   321  	log.Info().Msg("Starting registry...")
   322  	err := registry.loadInitialState()
   323  	if err != nil {
   324  		log.Error().Err(err).Msg("Could not start registry")
   325  	}
   326  }
   327  
   328  func (registry *contractRegistry) loadInitialState() error {
   329  	log.Debug().Msg("Loading initial state")
   330  	registry.lock.Lock()
   331  	defer registry.lock.Unlock()
   332  
   333  	entries, err := registry.storage.GetAll()
   334  	if err != nil {
   335  		return errors.Wrap(err, "Could not fetch previous registrations")
   336  	}
   337  
   338  	for i := range entries {
   339  		switch entries[i].RegistrationStatus {
   340  		case RegistrationError, InProgress:
   341  			entry := entries[i]
   342  			err := registry.handleUnregisteredIdentityInitialLoad(entry.ChainID, entry.Identity)
   343  			if err != nil {
   344  				return errors.Wrapf(err, "could not check %q registration status", entries[i].Identity)
   345  			}
   346  		default:
   347  			log.Debug().Msgf("Identity %q already registered, skipping", entries[i].Identity)
   348  		}
   349  	}
   350  
   351  	return nil
   352  }
   353  
   354  func (registry *contractRegistry) getProviderChannelAddressBytes(hermesAddress common.Address, providerIdentity identity.Identity) ([32]byte, error) {
   355  	providerAddress := providerIdentity.ToCommonAddress()
   356  	addressBytes := [32]byte{}
   357  
   358  	addr, err := crypto.GenerateProviderChannelID(providerAddress.Hex(), hermesAddress.Hex())
   359  	if err != nil {
   360  		return addressBytes, errors.Wrap(err, "could not generate channel address")
   361  	}
   362  
   363  	padded := crypto.Pad(common.FromHex(addr), 32)
   364  	copy(addressBytes[:], padded)
   365  
   366  	return addressBytes, nil
   367  }
   368  
   369  func (registry *contractRegistry) handleUnregisteredIdentityInitialLoad(chainID int64, id identity.Identity) error {
   370  	status, err := registry.GetRegistrationStatus(chainID, id)
   371  	if err != nil {
   372  		return errors.Wrap(err, "could not check status on blockchain")
   373  	}
   374  
   375  	if status == Registered {
   376  		return nil
   377  	}
   378  
   379  	registry.resyncWithBC(chainID, id.Address)
   380  	return nil
   381  }
   382  
   383  func (registry *contractRegistry) bcRegistrationStatus(chainID int64, id identity.Identity) (RegistrationStatus, error) {
   384  	reg, err := registry.ap.GetRegistryAddress(chainID)
   385  	if err != nil {
   386  		log.Error().Err(err).Msg("could not get registry address")
   387  		return RegistrationError, err
   388  	}
   389  
   390  	hermes, err := registry.ap.GetActiveHermes(chainID)
   391  	if err != nil {
   392  		log.Error().Err(err).Msg("could not get hermes address")
   393  		return RegistrationError, err
   394  	}
   395  
   396  	contract, err := bindings.NewRegistryCaller(reg, registry.ethC)
   397  	if err != nil {
   398  		return RegistrationError, fmt.Errorf("could not get registry caller %w", err)
   399  	}
   400  
   401  	contractSession := &bindings.RegistryCallerSession{
   402  		Contract: contract,
   403  		CallOpts: bind.CallOpts{
   404  			Pending: false, //we want to find out true registration status - not pending transactions
   405  		},
   406  	}
   407  
   408  	hermesContract, err := bindings.NewHermesImplementationCaller(hermes, registry.ethC)
   409  	if err != nil {
   410  		return RegistrationError, fmt.Errorf("could not get hermes implementation caller %w", err)
   411  	}
   412  
   413  	hermesSession := &bindings.HermesImplementationCallerSession{
   414  		Contract: hermesContract,
   415  		CallOpts: bind.CallOpts{
   416  			Pending: false, //we want to find out true registration status - not pending transactions
   417  		},
   418  	}
   419  
   420  	registered, err := contractSession.IsRegistered(
   421  		common.HexToAddress(id.Address),
   422  	)
   423  	if err != nil {
   424  		return RegistrationError, errors.Wrap(err, "could not check registration status in bc")
   425  	}
   426  
   427  	if !registered {
   428  		return Unregistered, nil
   429  	}
   430  
   431  	providerAddressBytes, err := registry.getProviderChannelAddressBytes(hermes, id)
   432  	if err != nil {
   433  		return RegistrationError, errors.Wrap(err, "could not get provider channel address")
   434  	}
   435  
   436  	_, err = hermesSession.Channels(providerAddressBytes)
   437  	if err != nil {
   438  		return RegistrationError, errors.Wrap(err, "could not get provider channel")
   439  	}
   440  
   441  	return Registered, nil
   442  }
   443  
   444  // AppTopicEthereumClientReconnected indicates that the ethereum client has reconnected.
   445  var AppTopicEthereumClientReconnected = "ether-client-reconnect"
   446  
   447  func (registry *contractRegistry) handleEtherClientReconnect(_ interface{}) {
   448  	err := registry.loadInitialState()
   449  	if err != nil {
   450  		log.Error().Err(err).Msg("could not resubscribe to identity status changes")
   451  	}
   452  }