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 }