github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/tequilapi/endpoints/identities.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 package endpoints 19 20 import ( 21 "encoding/json" 22 "fmt" 23 "math/big" 24 "net/http" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/gin-gonic/gin" 28 "github.com/mysteriumnetwork/go-rest/apierror" 29 "github.com/mysteriumnetwork/node/consumer/migration" 30 "github.com/mysteriumnetwork/payments/client" 31 "github.com/pkg/errors" 32 33 "github.com/mysteriumnetwork/node/config" 34 "github.com/mysteriumnetwork/node/core/beneficiary" 35 "github.com/mysteriumnetwork/node/identity" 36 "github.com/mysteriumnetwork/node/identity/registry" 37 identity_selector "github.com/mysteriumnetwork/node/identity/selector" 38 pingpong_event "github.com/mysteriumnetwork/node/session/pingpong/event" 39 "github.com/mysteriumnetwork/node/tequilapi/contract" 40 "github.com/mysteriumnetwork/node/tequilapi/middlewares" 41 "github.com/mysteriumnetwork/node/tequilapi/utils" 42 "github.com/rs/zerolog/log" 43 ) 44 45 type balanceProvider interface { 46 GetBalance(chainID int64, id identity.Identity) *big.Int 47 ForceBalanceUpdateCached(chainID int64, id identity.Identity) *big.Int 48 } 49 50 type earningsProvider interface { 51 GetEarningsDetailed(chainID int64, id identity.Identity) *pingpong_event.EarningsDetailed 52 } 53 54 type beneficiaryProvider interface { 55 GetBeneficiary(identity common.Address) (common.Address, error) 56 } 57 58 type providerChannel interface { 59 GetProviderChannel(chainID int64, hermesAddress common.Address, provider common.Address, pending bool) (client.ProviderChannel, error) 60 } 61 62 type identityMover interface { 63 Import(blob []byte, currPass, newPass string) (identity.Identity, error) 64 Export(address, currPass, newPass string) ([]byte, error) 65 } 66 67 type identitiesAPI struct { 68 mover identityMover 69 idm identity.Manager 70 selector identity_selector.Handler 71 registry registry.IdentityRegistry 72 addressProvider AddressProvider 73 balanceProvider balanceProvider 74 earningsProvider earningsProvider 75 bc providerChannel 76 transactor Transactor 77 bprovider beneficiaryProvider 78 beneficiaryStorage beneficiary.BeneficiaryStorage 79 hermesMigrator *migration.HermesMigrator 80 } 81 82 // AddressProvider provides sc addresses. 83 type AddressProvider interface { 84 GetActiveHermes(chainID int64) (common.Address, error) 85 GetActiveChannelAddress(chainID int64, id common.Address) (common.Address, error) 86 GetKnownHermeses(chainID int64) ([]common.Address, error) 87 GetHermesChannelAddress(chainID int64, id, hermesAddr common.Address) (common.Address, error) 88 } 89 90 // swagger:operation GET /identities Identity listIdentities 91 // 92 // --- 93 // summary: Returns identities 94 // description: Returns list of identities 95 // responses: 96 // 200: 97 // description: List of identities 98 // schema: 99 // "$ref": "#/definitions/ListIdentitiesResponse" 100 func (ia *identitiesAPI) List(c *gin.Context) { 101 ids := ia.idm.GetIdentities() 102 idsDTO := contract.NewIdentityListResponse(ids) 103 utils.WriteAsJSON(idsDTO, c.Writer) 104 } 105 106 // swagger:operation PUT /identities/current Identity currentIdentity 107 // 108 // --- 109 // summary: Returns my current identity 110 // description: Tries to retrieve the last used identity, the first identity, or creates and returns a new identity 111 // parameters: 112 // - in: body 113 // name: body 114 // description: Parameter in body (passphrase) required for creating new identity 115 // schema: 116 // $ref: "#/definitions/IdentityCurrentRequestDTO" 117 // responses: 118 // 200: 119 // description: Unlocked identity returned 120 // schema: 121 // "$ref": "#/definitions/IdentityRefDTO" 122 // 400: 123 // description: Failed to parse or request validation failed 124 // schema: 125 // "$ref": "#/definitions/APIError" 126 // 500: 127 // description: Internal server error 128 // schema: 129 // "$ref": "#/definitions/APIError" 130 func (ia *identitiesAPI) Current(c *gin.Context) { 131 var req contract.IdentityCurrentRequest 132 err := json.NewDecoder(c.Request.Body).Decode(&req) 133 if err != nil { 134 c.Error(apierror.ParseFailed()) 135 return 136 } 137 138 if err := req.Validate(); err != nil { 139 c.Error(err) 140 return 141 } 142 143 idAddress := "" 144 if req.Address != nil { 145 idAddress = *req.Address 146 } 147 148 chainID := config.GetInt64(config.FlagChainID) 149 id, err := ia.selector.UseOrCreate(idAddress, *req.Passphrase, chainID) 150 151 if err != nil { 152 c.Error(apierror.Internal("Failed to use/create ID: "+err.Error(), contract.ErrCodeIDUseOrCreate)) 153 return 154 } 155 156 idDTO := contract.NewIdentityDTO(id) 157 utils.WriteAsJSON(idDTO, c.Writer) 158 } 159 160 // swagger:operation POST /identities Identity createIdentity 161 // 162 // --- 163 // summary: Creates new identity 164 // description: Creates identity and stores in keystore encrypted with passphrase 165 // parameters: 166 // - in: body 167 // name: body 168 // description: Parameter in body (passphrase) required for creating new identity 169 // schema: 170 // $ref: "#/definitions/IdentityCreateRequestDTO" 171 // responses: 172 // 200: 173 // description: Identity created 174 // schema: 175 // "$ref": "#/definitions/IdentityRefDTO" 176 // 400: 177 // description: Failed to parse or request validation failed 178 // schema: 179 // "$ref": "#/definitions/APIError" 180 // 500: 181 // description: Internal server error 182 // schema: 183 // "$ref": "#/definitions/APIError" 184 func (ia *identitiesAPI) Create(c *gin.Context) { 185 var req contract.IdentityCreateRequest 186 err := json.NewDecoder(c.Request.Body).Decode(&req) 187 if err != nil { 188 c.Error(apierror.ParseFailed()) 189 return 190 } 191 192 if err := req.Validate(); err != nil { 193 c.Error(err) 194 return 195 } 196 197 id, err := ia.idm.CreateNewIdentity(*req.Passphrase) 198 if err != nil { 199 c.Error(apierror.Internal("Failed to create ID", contract.ErrCodeIDCreate)) 200 return 201 } 202 203 idDTO := contract.NewIdentityDTO(id) 204 utils.WriteAsJSON(idDTO, c.Writer) 205 } 206 207 // swagger:operation POST /export export Identity 208 // 209 // --- 210 // summary: Exports a given identity 211 // description: Creates identity and stores in keystore encrypted with passphrase 212 // parameters: 213 // - in: body 214 // name: body 215 // description: Parameter in body (passphrase) required for creating new identity 216 // schema: 217 // $ref: "#/definitions/IdentityExportRequestDTO" 218 // responses: 219 // 200: 220 // description: Identity created 221 // schema: 222 // "$ref": "#/definitions/IdentityExportResponseDTO" 223 // 400: 224 // description: Failed to parse or request validation failed 225 // schema: 226 // "$ref": "#/definitions/APIError" 227 // 500: 228 // description: Internal server error 229 // schema: 230 // "$ref": "#/definitions/APIError" 231 func (ia *identitiesAPI) Export(c *gin.Context) { 232 var req contract.IdentityExportRequest 233 err := json.NewDecoder(c.Request.Body).Decode(&req) 234 if err != nil { 235 c.Error(apierror.ParseFailed()) 236 return 237 } 238 239 if err := req.Validate(); err != nil { 240 c.Error(err) 241 return 242 } 243 244 resp, err := ia.mover.Export(req.Identity, "", req.NewPassphrase) 245 if err != nil { 246 c.Error(err) 247 return 248 } 249 c.Writer.Write(resp) 250 } 251 252 // swagger:operation PUT /identities/{id}/unlock Identity unlockIdentity 253 // 254 // --- 255 // summary: Unlocks identity 256 // description: Uses passphrase to decrypt identity stored in keystore 257 // parameters: 258 // - in: path 259 // name: id 260 // description: Identity stored in keystore 261 // type: string 262 // required: true 263 // - in: body 264 // name: body 265 // description: Parameter in body (passphrase) required for unlocking identity 266 // schema: 267 // $ref: "#/definitions/IdentityUnlockRequestDTO" 268 // responses: 269 // 202: 270 // description: Identity unlocked 271 // 400: 272 // description: Failed to parse or request validation failed 273 // schema: 274 // "$ref": "#/definitions/APIError" 275 // 403: 276 // description: Unlock failed 277 // schema: 278 // "$ref": "#/definitions/APIError" 279 // 404: 280 // description: ID not found 281 // schema: 282 // "$ref": "#/definitions/APIError" 283 func (ia *identitiesAPI) Unlock(c *gin.Context) { 284 address := c.Param("id") 285 id, err := ia.idm.GetIdentity(address) 286 if err != nil { 287 c.Error(apierror.NotFound("ID not found")) 288 return 289 } 290 291 var req contract.IdentityUnlockRequest 292 err = json.NewDecoder(c.Request.Body).Decode(&req) 293 if err != nil { 294 c.Error(apierror.ParseFailed()) 295 return 296 } 297 298 if err := req.Validate(); err != nil { 299 c.Error(err) 300 return 301 } 302 303 chainID := config.GetInt64(config.FlagChainID) 304 err = ia.idm.Unlock(chainID, id.Address, *req.Passphrase) 305 if err != nil { 306 c.Error(apierror.Forbidden("Unlock failed", contract.ErrCodeIDUnlock)) 307 return 308 } 309 c.Status(http.StatusAccepted) 310 } 311 312 // swagger:operation PUT /identities/{id}/balance/refresh Identity balance 313 // 314 // --- 315 // summary: Refresh balance of given identity 316 // description: Refresh balance of given identity 317 // parameters: 318 // - in: path 319 // name: id 320 // description: hex address of identity 321 // type: string 322 // required: true 323 // responses: 324 // 200: 325 // description: Updated balance 326 // schema: 327 // "$ref": "#/definitions/BalanceDTO" 328 // 404: 329 // description: ID not found 330 // schema: 331 // "$ref": "#/definitions/APIError" 332 func (ia *identitiesAPI) BalanceRefresh(c *gin.Context) { 333 address := c.Param("id") 334 id, err := ia.idm.GetIdentity(address) 335 if err != nil { 336 c.Error(apierror.NotFound("Identity not found")) 337 return 338 } 339 chainID := config.GetInt64(config.FlagChainID) 340 balance := ia.balanceProvider.ForceBalanceUpdateCached(chainID, id) 341 status := contract.BalanceDTO{ 342 Balance: balance, 343 BalanceTokens: contract.NewTokens(balance), 344 } 345 utils.WriteAsJSON(status, c.Writer) 346 } 347 348 // swagger:operation GET /identities/{id} Identity getIdentity 349 // 350 // --- 351 // summary: Get identity 352 // description: Provide identity details 353 // parameters: 354 // - in: path 355 // name: id 356 // description: hex address of identity 357 // type: string 358 // required: true 359 // responses: 360 // 200: 361 // description: Identity retrieved 362 // schema: 363 // "$ref": "#/definitions/IdentityRefDTO" 364 // 404: 365 // description: ID not found 366 // schema: 367 // "$ref": "#/definitions/APIError" 368 // 500: 369 // description: Internal server error 370 // schema: 371 // "$ref": "#/definitions/APIError" 372 func (ia *identitiesAPI) Get(c *gin.Context) { 373 address := c.Param("id") 374 id, err := ia.idm.GetIdentity(address) 375 if err != nil { 376 c.Error(apierror.NotFound("Identity not found")) 377 return 378 } 379 380 chainID := config.GetInt64(config.FlagChainID) 381 regStatus, err := ia.registry.GetRegistrationStatus(chainID, id) 382 if err != nil { 383 c.Error(apierror.Internal("Failed to check ID registration status: "+err.Error(), contract.ErrCodeIDRegistrationCheck)) 384 return 385 } 386 387 channelAddress, err := ia.addressProvider.GetActiveChannelAddress(chainID, id.ToCommonAddress()) 388 if err != nil { 389 c.Error(apierror.Internal("Failed to calculate channel address: "+err.Error(), contract.ErrCodeIDCalculateAddress)) 390 return 391 } 392 393 defaultHermesID, err := ia.addressProvider.GetActiveHermes(chainID) 394 if err != nil { 395 c.Error(apierror.Internal("Could not get active hermes: "+err.Error(), contract.ErrCodeActiveHermes)) 396 return 397 } 398 399 var stake = new(big.Int) 400 if regStatus == registry.Registered { 401 data, err := ia.bc.GetProviderChannel(chainID, defaultHermesID, common.HexToAddress(address), false) 402 if err != nil { 403 c.Error(apierror.Internal("Failed to check blockchain registration status: "+err.Error(), contract.ErrCodeIDBlockchainRegistrationCheck)) 404 return 405 } 406 stake = data.Stake 407 } 408 409 balance := ia.balanceProvider.GetBalance(chainID, id) 410 earnings := ia.earningsProvider.GetEarningsDetailed(chainID, id) 411 412 settlementsPerHermes := make(map[string]contract.EarningsDTO) 413 for h, earn := range earnings.PerHermes { 414 settlementsPerHermes[h.Hex()] = contract.EarningsDTO{ 415 Earnings: contract.NewTokens(earn.UnsettledBalance), 416 EarningsTotal: contract.NewTokens(earn.LifetimeBalance), 417 } 418 } 419 420 status := contract.IdentityDTO{ 421 Address: address, 422 RegistrationStatus: regStatus.String(), 423 ChannelAddress: channelAddress.Hex(), 424 Balance: balance, 425 BalanceTokens: contract.NewTokens(balance), 426 Earnings: earnings.Total.UnsettledBalance, 427 EarningsTokens: contract.NewTokens(earnings.Total.UnsettledBalance), 428 EarningsTotal: earnings.Total.LifetimeBalance, 429 EarningsTotalTokens: contract.NewTokens(earnings.Total.LifetimeBalance), 430 Stake: stake, 431 HermesID: defaultHermesID.Hex(), 432 EarningsPerHermes: contract.NewEarningsPerHermesDTO(earnings.PerHermes), 433 } 434 utils.WriteAsJSON(status, c.Writer) 435 } 436 437 // swagger:operation GET /identities/{id}/registration Identity identityRegistration 438 // 439 // --- 440 // summary: Provide identity registration status 441 // description: Provides registration status for given identity, if identity is not registered - provides additional data required for identity registration 442 // parameters: 443 // - in: path 444 // name: id 445 // description: hex address of identity 446 // type: string 447 // required: true 448 // responses: 449 // 200: 450 // description: Status retrieved 451 // schema: 452 // "$ref": "#/definitions/IdentityRegistrationResponseDTO" 453 // 404: 454 // description: ID not found 455 // schema: 456 // "$ref": "#/definitions/APIError" 457 // 500: 458 // description: Internal server error 459 // schema: 460 // "$ref": "#/definitions/APIError" 461 func (ia *identitiesAPI) RegistrationStatus(c *gin.Context) { 462 address := c.Param("id") 463 id, err := ia.idm.GetIdentity(address) 464 if err != nil { 465 c.Error(apierror.NotFound("ID not found")) 466 return 467 } 468 469 regStatus, err := ia.registry.GetRegistrationStatus(config.GetInt64(config.FlagChainID), id) 470 if err != nil { 471 c.Error(apierror.Internal("Failed to check ID registration status", contract.ErrCodeIDRegistrationCheck)) 472 return 473 } 474 475 registrationDataDTO := &contract.IdentityRegistrationResponse{ 476 Status: regStatus.String(), 477 Registered: regStatus.Registered(), 478 } 479 utils.WriteAsJSON(registrationDataDTO, c.Writer) 480 } 481 482 // swagger:operation GET /identities/{id}/beneficiary Identity beneficiary address 483 // 484 // --- 485 // summary: Provide identity beneficiary address 486 // description: Provides beneficiary address for given identity 487 // parameters: 488 // - in: path 489 // name: id 490 // description: hex address of identity 491 // type: string 492 // required: true 493 // responses: 494 // 200: 495 // description: Beneficiary retrieved 496 // schema: 497 // "$ref": "#/definitions/IdentityBeneficiaryResponseDTO" 498 // 500: 499 // description: Internal server error 500 // schema: 501 // "$ref": "#/definitions/APIError" 502 func (ia *identitiesAPI) Beneficiary(c *gin.Context) { 503 address := c.Param("id") 504 identity := common.HexToAddress(address) 505 beneficiary, err := ia.bprovider.GetBeneficiary(identity) 506 if err != nil { 507 utils.ForwardError(c, err, apierror.Internal("Failed to get beneficiary address", contract.ErrCodeBeneficiaryGet)) 508 return 509 } 510 511 isChannel, err := isBenenficiarySetToChannel(ia.addressProvider, config.GetInt64(config.FlagChainID), identity, beneficiary) 512 if err != nil { 513 utils.ForwardError(c, err, apierror.Internal("Failed to check if beneficiary is set to channel address", contract.ErrCodeBeneficiaryGet)) 514 return 515 } 516 517 registrationDataDTO := &contract.IdentityBeneficiaryResponse{ 518 Beneficiary: beneficiary.Hex(), 519 IsChannelAddress: isChannel, 520 } 521 utils.WriteAsJSON(registrationDataDTO, c.Writer) 522 } 523 524 // swagger:operation POST /identities-import Identities importIdentity 525 // 526 // --- 527 // summary: Imports a given identity. 528 // description: Imports a given identity returning it is a blob of text which can later be used to import it back. 529 // parameters: 530 // - in: body 531 // name: body 532 // description: Parameter in body used to import an identity. 533 // schema: 534 // $ref: "#/definitions/IdentityImportRequest" 535 // responses: 536 // 200: 537 // description: Unlocked identity returned 538 // schema: 539 // "$ref": "#/definitions/IdentityRefDTO" 540 // 400: 541 // description: Failed to parse or request validation failed 542 // schema: 543 // "$ref": "#/definitions/APIError" 544 // 422: 545 // description: Unable to process the request at this point 546 // schema: 547 // "$ref": "#/definitions/APIError" 548 // 500: 549 // description: Internal server error 550 // schema: 551 // "$ref": "#/definitions/APIError" 552 func (ia *identitiesAPI) Import(c *gin.Context) { 553 var req contract.IdentityImportRequest 554 if err := json.NewDecoder(c.Request.Body).Decode(&req); err != nil { 555 c.Error(apierror.ParseFailed()) 556 return 557 } 558 559 if err := req.Validate(); err != nil { 560 c.Error(err) 561 return 562 } 563 564 id, err := ia.mover.Import(req.Data, req.CurrentPassphrase, req.NewPassphrase) 565 if err != nil { 566 c.Error(apierror.Unprocessable(fmt.Sprintf("Failed to import identity: %s", err), contract.ErrCodeIDImport)) 567 return 568 } 569 570 if req.SetDefault { 571 if err := ia.selector.SetDefault(id.Address); err != nil { 572 c.Error(apierror.Unprocessable(fmt.Sprintf("Failed to set default identity: %s", err), contract.ErrCodeIDSetDefault)) 573 return 574 } 575 } 576 577 idDTO := contract.NewIdentityDTO(id) 578 utils.WriteAsJSON(idDTO, c.Writer) 579 } 580 581 // swagger:operation GET /identities/:id/beneficiary-async 582 // 583 // --- 584 // summary: Get beneficiary address 585 // description: Get beneficiary address stored locally 586 // parameters: 587 // - in: path 588 // name: id 589 // description: Identity stored in keystore 590 // type: string 591 // required: true 592 // responses: 593 // 200: 594 // description: Local beneficiary address returned 595 // schema: 596 // "$ref": "#/definitions/BeneficiaryAddressRequest" 597 // 400: 598 // description: Failed to parse or request validation failed 599 // schema: 600 // "$ref": "#/definitions/APIError" 601 // 500: 602 // description: Internal server error 603 // schema: 604 // "$ref": "#/definitions/APIError" 605 func (ia *identitiesAPI) GetBeneficiaryAddressAsync(c *gin.Context) { 606 id := c.Param("id") 607 addr, err := ia.beneficiaryStorage.Address(id) 608 if err != nil { 609 if errors.Is(err, beneficiary.ErrNotFound) { 610 utils.WriteAsJSON(contract.BeneficiaryAddressRequest{}, c.Writer) 611 return 612 } 613 c.Error(apierror.Internal("Failed to get beneficiary address", contract.ErrCodeIDGetBeneficiaryAddress)) 614 return 615 } 616 617 utils.WriteAsJSON(contract.BeneficiaryAddressRequest{Address: addr}, c.Writer) 618 } 619 620 // swagger:operation POST /identities/:id/beneficiary-async 621 // 622 // --- 623 // summary: Save beneficiary address to local storage 624 // description: Stores beneficiary address locally 625 // parameters: 626 // - in: path 627 // name: id 628 // description: Identity stored in keystore 629 // type: string 630 // required: true 631 // - in: body 632 // name: body 633 // description: Beneficiary address request. 634 // schema: 635 // $ref: "#/definitions/BeneficiaryAddressRequest" 636 // responses: 637 // 200: 638 // description: Local beneficiary address returned 639 // schema: 640 // "$ref": "#/definitions/BeneficiaryChangeRequest" 641 // 400: 642 // description: Failed to parse or request validation failed 643 // schema: 644 // "$ref": "#/definitions/APIError" 645 func (ia *identitiesAPI) SaveBeneficiaryAddressAsync(c *gin.Context) { 646 var par contract.BeneficiaryAddressRequest 647 if err := json.NewDecoder(c.Request.Body).Decode(&par); err != nil { 648 c.Error(apierror.ParseFailed()) 649 return 650 } 651 652 id := c.Param("id") 653 err := ia.beneficiaryStorage.Save(id, par.Address) 654 if err != nil { 655 c.Error(apierror.BadRequest("Invalid address", contract.ErrCodeIDSaveBeneficiaryAddress)) 656 return 657 } 658 659 utils.WriteAsJSON(par, c.Writer) 660 } 661 662 // swagger:operation POST /identities/:id/migrate-hermes 663 // 664 // --- 665 // summary: Migrate Hermes 666 // description: Migrate from old to new Hermes 667 // parameters: 668 // - in: path 669 // name: id 670 // description: Identity stored in keystore 671 // type: string 672 // required: true 673 // responses: 674 // 200: 675 // description: Successfully migrated 676 // 403: 677 // schema: 678 // "$ref": "#/definitions/APIError" 679 // 500: 680 // description: Internal server error 681 // schema: 682 // "$ref": "#/definitions/APIError" 683 func (ia *identitiesAPI) MigrateHermes(c *gin.Context) { 684 id := c.Param("id") 685 if !ia.idm.IsUnlocked(id) { 686 c.Error(apierror.Forbidden("Identity is locked", contract.ErrCodeIDLocked)) 687 return 688 } 689 err := ia.hermesMigrator.Start(id) 690 if err != nil { 691 c.Error(apierror.Internal(err.Error(), contract.ErrCodeHermesMigration)) 692 log.Err(err).Msgf("could not migrate identity %s", id) 693 return 694 } 695 696 c.Status(http.StatusOK) 697 } 698 699 // swagger:operation GEY /identities/:id/migrate-hermes/status 700 // 701 // --- 702 // summary: Migration Hermes status 703 // description: Migrate from old to new Hermes 704 // parameters: 705 // - in: path 706 // name: id 707 // description: Identity stored in keystore 708 // type: string 709 // required: true 710 // responses: 711 // 200: 712 // description: Successfully migrated 713 // schema: 714 // "$ref": "#/definitions/MigrationStatusResponse" 715 // 500: 716 // description: Internal server error 717 // schema: 718 // "$ref": "#/definitions/APIError" 719 func (ia *identitiesAPI) MigrationHermesStatus(c *gin.Context) { 720 id := c.Param("id") 721 r, err := ia.hermesMigrator.IsMigrationRequired(id) 722 if err != nil { 723 c.Error(apierror.Internal("Failed to check migration status", contract.ErrCodeCheckHermesMigrationStatus)) 724 log.Err(err).Msgf("could not check Hermes migration status, id: %s", id) 725 return 726 } 727 728 var status contract.MigrationStatus 729 if r { 730 status = contract.MigrationStatusRequired 731 } else { 732 status = contract.MigrationStatusFinished 733 } 734 735 utils.WriteAsJSON(contract.MigrationStatusResponse{Status: status}, c.Writer) 736 } 737 738 func isBenenficiarySetToChannel(addressProvider addressProvider, chainID int64, identity, beneficiary common.Address) (bool, error) { 739 hermeses, err := addressProvider.GetKnownHermeses(chainID) 740 if err != nil { 741 return false, fmt.Errorf("failed to get list of known hermeses: %w", err) 742 } 743 for _, h := range hermeses { 744 channelAddr, err := addressProvider.GetHermesChannelAddress(chainID, identity, h) 745 if err != nil { 746 log.Err(err).Msgf("could not generate channel address for chain %d and hermes %s", chainID, h.Hex()) 747 return false, fmt.Errorf("failed to generate channel address: %w", err) 748 } 749 if channelAddr == beneficiary { 750 return true, nil 751 } 752 } 753 return false, nil 754 } 755 756 // AddRoutesForIdentities creates /identities endpoint on tequilapi service 757 func AddRoutesForIdentities( 758 idm identity.Manager, 759 selector identity_selector.Handler, 760 registry registry.IdentityRegistry, 761 balanceProvider balanceProvider, 762 addressProvider *client.MultiChainAddressProvider, 763 earningsProvider earningsProvider, 764 bc providerChannel, 765 transactor Transactor, 766 bprovider beneficiaryProvider, 767 mover identityMover, 768 addressStorage beneficiary.BeneficiaryStorage, 769 hermesMigrator *migration.HermesMigrator, 770 ) func(*gin.Engine) error { 771 idAPI := &identitiesAPI{ 772 mover: mover, 773 idm: idm, 774 selector: selector, 775 registry: registry, 776 balanceProvider: balanceProvider, 777 addressProvider: addressProvider, 778 earningsProvider: earningsProvider, 779 bc: bc, 780 transactor: transactor, 781 bprovider: bprovider, 782 beneficiaryStorage: addressStorage, 783 hermesMigrator: hermesMigrator, 784 } 785 return func(e *gin.Engine) error { 786 identityGroup := e.Group("/identities") 787 { 788 identityGroup.GET("", idAPI.List) 789 identityGroup.POST("", idAPI.Create) 790 identityGroup.PUT("/current", idAPI.Current) 791 identityGroup.GET("/:id", idAPI.Get) 792 identityGroup.GET("/:id/status", idAPI.Get) 793 identityGroup.PUT("/:id/unlock", idAPI.Unlock) 794 identityGroup.GET("/:id/registration", idAPI.RegistrationStatus) 795 identityGroup.GET("/:id/beneficiary", idAPI.Beneficiary) 796 identityGroup.GET("/:id/beneficiary-async", idAPI.GetBeneficiaryAddressAsync) 797 identityGroup.POST("/:id/beneficiary-async", idAPI.SaveBeneficiaryAddressAsync) 798 identityGroup.PUT("/:id/balance/refresh", idAPI.BalanceRefresh) 799 identityGroup.POST("/:id/migrate-hermes", idAPI.MigrateHermes) 800 identityGroup.GET("/:id/migrate-hermes/status", idAPI.MigrationHermesStatus) 801 identityGroup.POST("/export", middlewares.NewLocalhostOnlyFilter(), idAPI.Export) 802 803 } 804 e.POST("/identities-import", idAPI.Import) 805 return nil 806 } 807 }