github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/tequilapi/client/client.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 client 19 20 import ( 21 "fmt" 22 "io" 23 "math/big" 24 "net/http" 25 "net/url" 26 "strconv" 27 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/pkg/errors" 30 31 "github.com/mysteriumnetwork/go-rest/apierror" 32 "github.com/mysteriumnetwork/node/identity" 33 "github.com/mysteriumnetwork/node/tequilapi/contract" 34 "github.com/mysteriumnetwork/payments/exchange" 35 ) 36 37 // NewClient returns a new instance of Client 38 func NewClient(ip string, port int) *Client { 39 return &Client{ 40 http: newHTTPClient( 41 fmt.Sprintf("http://%s:%d", ip, port), 42 "goclient-v0.1", 43 ), 44 } 45 } 46 47 // Client is able perform remote requests to Tequilapi server 48 type Client struct { 49 http httpClientInterface 50 } 51 52 // AuthAuthenticate authenticates user and issues auth token 53 func (client *Client) AuthAuthenticate(request contract.AuthRequest) (res contract.AuthResponse, err error) { 54 response, err := client.http.Post("/auth/authenticate", request) 55 if err != nil { 56 return res, err 57 } 58 defer response.Body.Close() 59 60 err = parseResponseJSON(response, &res) 61 if err != nil { 62 return res, err 63 } 64 65 client.http.SetToken(res.Token) 66 return res, nil 67 } 68 69 // AuthLogin authenticates user and sets cookie with issued auth token 70 func (client *Client) AuthLogin(request contract.AuthRequest) (res contract.AuthResponse, err error) { 71 response, err := client.http.Post("/auth/login", request) 72 if err != nil { 73 return res, err 74 } 75 defer response.Body.Close() 76 77 err = parseResponseJSON(response, &res) 78 if err != nil { 79 return res, err 80 } 81 82 client.http.SetToken(res.Token) 83 return res, nil 84 } 85 86 // AuthLogout Clears authentication cookie 87 func (client *Client) AuthLogout() error { 88 response, err := client.http.Delete("/auth/logout", nil) 89 if err != nil { 90 return err 91 } 92 defer response.Body.Close() 93 94 return nil 95 } 96 97 // AuthChangePassword changes user password 98 func (client *Client) AuthChangePassword(request contract.ChangePasswordRequest) error { 99 response, err := client.http.Put("/auth/password", request) 100 if err != nil { 101 return err 102 } 103 defer response.Body.Close() 104 105 return nil 106 } 107 108 // ImportIdentity sends a request to import a given identity. 109 func (client *Client) ImportIdentity(blob []byte, passphrase string, setDefault bool) (id contract.IdentityRefDTO, err error) { 110 response, err := client.http.Post("identities-import", contract.IdentityImportRequest{ 111 Data: blob, 112 CurrentPassphrase: passphrase, 113 SetDefault: setDefault, 114 }) 115 if err != nil { 116 return 117 } 118 defer response.Body.Close() 119 120 err = parseResponseJSON(response, &id) 121 return id, err 122 } 123 124 // GetIdentities returns a list of client identities 125 func (client *Client) GetIdentities() (ids []contract.IdentityRefDTO, err error) { 126 response, err := client.http.Get("identities", url.Values{}) 127 if err != nil { 128 return 129 } 130 defer response.Body.Close() 131 132 var list contract.ListIdentitiesResponse 133 err = parseResponseJSON(response, &list) 134 135 return list.Identities, err 136 } 137 138 // NewIdentity creates a new client identity 139 func (client *Client) NewIdentity(passphrase string) (id contract.IdentityRefDTO, err error) { 140 response, err := client.http.Post("identities", contract.IdentityCreateRequest{Passphrase: &passphrase}) 141 if err != nil { 142 return 143 } 144 defer response.Body.Close() 145 146 err = parseResponseJSON(response, &id) 147 return id, err 148 } 149 150 // CurrentIdentity unlocks and returns the last used, new or first identity 151 func (client *Client) CurrentIdentity(identity, passphrase string) (id contract.IdentityRefDTO, err error) { 152 response, err := client.http.Put("identities/current", contract.IdentityCurrentRequest{ 153 Address: &identity, 154 Passphrase: &passphrase, 155 }) 156 if err != nil { 157 return 158 } 159 defer response.Body.Close() 160 161 err = parseResponseJSON(response, &id) 162 return id, err 163 } 164 165 // BalanceRefresh forces a balance refresh if possible and returns the latest balance. 166 func (client *Client) BalanceRefresh(identityAddress string) (b contract.BalanceDTO, err error) { 167 path := fmt.Sprintf("identities/%s/balance/refresh", identityAddress) 168 169 response, err := client.http.Put(path, nil) 170 if err != nil { 171 return b, err 172 } 173 defer response.Body.Close() 174 175 err = parseResponseJSON(response, &b) 176 return b, err 177 } 178 179 // Identity returns identity status with cached balance 180 func (client *Client) Identity(identityAddress string) (id contract.IdentityDTO, err error) { 181 path := fmt.Sprintf("identities/%s", identityAddress) 182 183 response, err := client.http.Get(path, nil) 184 if err != nil { 185 return id, err 186 } 187 defer response.Body.Close() 188 189 err = parseResponseJSON(response, &id) 190 return id, err 191 } 192 193 // IdentityRegistrationStatus returns information of identity needed to register it on blockchain 194 func (client *Client) IdentityRegistrationStatus(address string) (contract.IdentityRegistrationResponse, error) { 195 response, err := client.http.Get("identities/"+address+"/registration", url.Values{}) 196 if err != nil { 197 return contract.IdentityRegistrationResponse{}, err 198 } 199 defer response.Body.Close() 200 201 status := contract.IdentityRegistrationResponse{} 202 err = parseResponseJSON(response, &status) 203 return status, err 204 } 205 206 // GetTransactorFees returns the transactor fees 207 func (client *Client) GetTransactorFees() (contract.FeesDTO, error) { 208 fees := contract.FeesDTO{} 209 210 res, err := client.http.Get("transactor/fees", nil) 211 if err != nil { 212 return fees, err 213 } 214 defer res.Body.Close() 215 216 err = parseResponseJSON(res, &fees) 217 return fees, err 218 } 219 220 // RegisterIdentity registers identity 221 func (client *Client) RegisterIdentity(address, beneficiary string, token *string) error { 222 payload := contract.IdentityRegisterRequest{ 223 ReferralToken: token, 224 Beneficiary: beneficiary, 225 } 226 227 response, err := client.http.Post("identities/"+address+"/register", payload) 228 if err != nil { 229 return err 230 } 231 defer response.Body.Close() 232 233 switch response.StatusCode { 234 case http.StatusOK, http.StatusAccepted: 235 default: 236 return fmt.Errorf("expected 200 or 202 got %v", response.StatusCode) 237 } 238 239 return nil 240 } 241 242 // GetRegistrationPaymentStatus returns the registration payment status 243 func (client *Client) GetRegistrationPaymentStatus(identity string) (contract.RegistrationPaymentResponse, error) { 244 resp := contract.RegistrationPaymentResponse{} 245 246 res, err := client.http.Get(fmt.Sprintf("v2/identities/%s/registration-payment", identity), nil) 247 if err != nil { 248 return resp, err 249 } 250 defer res.Body.Close() 251 252 err = parseResponseJSON(res, &resp) 253 return resp, err 254 } 255 256 // ConnectionCreate initiates a new connection to a host identified by providerID 257 func (client *Client) ConnectionCreate(consumerID, providerID, hermesID, serviceType string, options contract.ConnectOptions) (status contract.ConnectionInfoDTO, err error) { 258 response, err := client.http.Put("connection", contract.ConnectionCreateRequest{ 259 ConsumerID: consumerID, 260 ProviderID: providerID, 261 HermesID: hermesID, 262 ServiceType: serviceType, 263 ConnectOptions: options, 264 Filter: contract.ConnectionCreateFilter{ 265 IncludeMonitoringFailed: true, 266 }, 267 }) 268 if err != nil { 269 return contract.ConnectionInfoDTO{}, err 270 } 271 defer response.Body.Close() 272 273 err = parseResponseJSON(response, &status) 274 return status, err 275 } 276 277 // SmartConnectionCreate initiates a new connection to a host identified by filter 278 func (client *Client) SmartConnectionCreate(consumerID, hermesID, serviceType string, filter contract.ConnectionCreateFilter, options contract.ConnectOptions) (status contract.ConnectionInfoDTO, err error) { 279 response, err := client.http.Put("connection", contract.ConnectionCreateRequest{ 280 ConsumerID: consumerID, 281 Filter: filter, 282 HermesID: hermesID, 283 ServiceType: serviceType, 284 ConnectOptions: options, 285 }) 286 if err != nil { 287 return contract.ConnectionInfoDTO{}, err 288 } 289 defer response.Body.Close() 290 291 err = parseResponseJSON(response, &status) 292 return status, err 293 } 294 295 // ConnectionDestroy terminates current connection 296 func (client *Client) ConnectionDestroy(port int) (err error) { 297 url := fmt.Sprintf("connection?%s", url.Values{"id": []string{strconv.Itoa(port)}}.Encode()) 298 response, err := client.http.Delete(url, nil) 299 if err != nil { 300 return 301 } 302 defer response.Body.Close() 303 304 return nil 305 } 306 307 // ConnectionStatistics returns statistics about current connection 308 func (client *Client) ConnectionStatistics(sessionID ...string) (statistics contract.ConnectionStatisticsDTO, err error) { 309 response, err := client.http.Get("connection/statistics", url.Values{ 310 "id": sessionID, 311 }) 312 if err != nil { 313 return statistics, err 314 } 315 defer response.Body.Close() 316 317 err = parseResponseJSON(response, &statistics) 318 return statistics, err 319 } 320 321 // ConnectionTraffic returns traffic information about current connection 322 func (client *Client) ConnectionTraffic(sessionID ...string) (traffic contract.ConnectionTrafficDTO, err error) { 323 response, err := client.http.Get("connection/traffic", url.Values{ 324 "id": sessionID, 325 }) 326 if err != nil { 327 return traffic, err 328 } 329 defer response.Body.Close() 330 331 err = parseResponseJSON(response, &traffic) 332 return traffic, err 333 } 334 335 // ConnectionStatus returns connection status 336 func (client *Client) ConnectionStatus(port int) (status contract.ConnectionInfoDTO, err error) { 337 response, err := client.http.Get("connection", url.Values{"id": []string{strconv.Itoa(port)}}) 338 if err != nil { 339 return status, err 340 } 341 defer response.Body.Close() 342 343 err = parseResponseJSON(response, &status) 344 return status, err 345 } 346 347 // ConnectionIP returns public ip 348 func (client *Client) ConnectionIP() (ip contract.IPDTO, err error) { 349 response, err := client.http.Get("connection/ip", url.Values{}) 350 if err != nil { 351 return ip, err 352 } 353 defer response.Body.Close() 354 355 err = parseResponseJSON(response, &ip) 356 return ip, err 357 } 358 359 // ProxyIP returns public ip of the proxy. 360 func (client *Client) ProxyIP(proxyPort int) (ip contract.IPDTO, err error) { 361 response, err := client.http.Get("connection/proxy/ip", url.Values{"port": []string{strconv.Itoa(proxyPort)}}) 362 if err != nil { 363 return ip, err 364 } 365 defer response.Body.Close() 366 367 err = parseResponseJSON(response, &ip) 368 return ip, err 369 } 370 371 // ProxyLocation returns proxy location. 372 func (client *Client) ProxyLocation(proxyPort int) (location contract.LocationDTO, err error) { 373 response, err := client.http.Get("connection/proxy/location", url.Values{"port": []string{strconv.Itoa(proxyPort)}}) 374 if err != nil { 375 return location, err 376 } 377 defer response.Body.Close() 378 379 err = parseResponseJSON(response, &location) 380 return location, err 381 } 382 383 // ConnectionLocation returns current location 384 func (client *Client) ConnectionLocation() (location contract.LocationDTO, err error) { 385 response, err := client.http.Get("connection/location", url.Values{}) 386 if err != nil { 387 return location, err 388 } 389 defer response.Body.Close() 390 391 err = parseResponseJSON(response, &location) 392 return location, err 393 } 394 395 // Healthcheck returns a healthcheck info 396 func (client *Client) Healthcheck() (healthcheck contract.HealthCheckDTO, err error) { 397 response, err := client.http.Get("healthcheck", url.Values{}) 398 if err != nil { 399 return 400 } 401 402 defer response.Body.Close() 403 err = parseResponseJSON(response, &healthcheck) 404 return healthcheck, err 405 } 406 407 // OriginLocation returns original location 408 func (client *Client) OriginLocation() (location contract.LocationDTO, err error) { 409 response, err := client.http.Get("location", url.Values{}) 410 if err != nil { 411 return location, err 412 } 413 defer response.Body.Close() 414 415 err = parseResponseJSON(response, &location) 416 return location, err 417 } 418 419 // ProposalsByType fetches proposals by given type 420 func (client *Client) ProposalsByType(serviceType string) ([]contract.ProposalDTO, error) { 421 queryParams := url.Values{} 422 queryParams.Add("service_type", serviceType) 423 return client.proposals(queryParams) 424 } 425 426 // ProposalsByTypeWithWhitelisting fetches proposals by given type with all whitelisting options. 427 func (client *Client) ProposalsByTypeWithWhitelisting(serviceType string) ([]contract.ProposalDTO, error) { 428 queryParams := url.Values{} 429 queryParams.Add("service_type", serviceType) 430 queryParams.Add("access_policy", "all") 431 return client.proposals(queryParams) 432 } 433 434 // ProposalsByLocationAndService fetches proposals by given service and node location types. 435 func (client *Client) ProposalsByLocationAndService(serviceType, locationType, locationCountry string) ([]contract.ProposalDTO, error) { 436 queryParams := url.Values{} 437 queryParams.Add("service_type", serviceType) 438 queryParams.Add("ip_type", locationType) 439 queryParams.Add("location_country", locationCountry) 440 return client.proposals(queryParams) 441 } 442 443 // Proposals returns all available proposals for services 444 func (client *Client) Proposals() ([]contract.ProposalDTO, error) { 445 return client.proposals(url.Values{}) 446 } 447 448 // ProposalsNATCompatible returns proposals for services which we can connect to 449 func (client *Client) ProposalsNATCompatible() ([]contract.ProposalDTO, error) { 450 queryParams := url.Values{} 451 queryParams.Add("nat_compatibility", contract.AutoNATType) 452 return client.proposals(queryParams) 453 } 454 455 func (client *Client) proposals(query url.Values) ([]contract.ProposalDTO, error) { 456 response, err := client.http.Get("proposals", query) 457 if err != nil { 458 return []contract.ProposalDTO{}, err 459 } 460 defer response.Body.Close() 461 462 var proposals contract.ListProposalsResponse 463 err = parseResponseJSON(response, &proposals) 464 return proposals.Proposals, err 465 } 466 467 // Unlock allows using identity in following commands 468 func (client *Client) Unlock(identity, passphrase string) error { 469 payload := contract.IdentityUnlockRequest{ 470 Passphrase: &passphrase, 471 } 472 473 path := fmt.Sprintf("identities/%s/unlock", identity) 474 response, err := client.http.Put(path, payload) 475 if err != nil { 476 return err 477 } 478 defer response.Body.Close() 479 480 return nil 481 } 482 483 // SetBeneficiaryAsync store beneficiary address locally for identity. 484 func (client *Client) SetBeneficiaryAsync(identity, ethAddress string) error { 485 path := fmt.Sprintf("identities/%s/beneficiary-async", identity) 486 payload := contract.BeneficiaryAddressRequest{ 487 Address: ethAddress, 488 } 489 490 response, err := client.http.Post(path, payload) 491 if err != nil { 492 return err 493 } 494 defer response.Body.Close() 495 496 if response.StatusCode != http.StatusOK { 497 return fmt.Errorf("failed to save") 498 } 499 500 return nil 501 } 502 503 // GetBeneficiaryAsync gets locally saved beneficiary address. 504 func (client *Client) GetBeneficiaryAsync(identity string) (contract.BeneficiaryAddressRequest, error) { 505 path := fmt.Sprintf("identities/%s/beneficiary-async", identity) 506 res := contract.BeneficiaryAddressRequest{} 507 response, err := client.http.Get(path, nil) 508 if err != nil { 509 return res, err 510 } 511 defer response.Body.Close() 512 513 err = parseResponseJSON(response, &res) 514 return res, err 515 } 516 517 // Stop kills mysterium client 518 func (client *Client) Stop() error { 519 emptyPayload := struct{}{} 520 response, err := client.http.Post("stop", emptyPayload) 521 if err != nil { 522 return err 523 } 524 defer response.Body.Close() 525 526 return nil 527 } 528 529 // Sessions returns all sessions from history 530 func (client *Client) Sessions() (sessions contract.SessionListResponse, err error) { 531 response, err := client.http.Get("sessions", url.Values{}) 532 if err != nil { 533 return sessions, err 534 } 535 defer response.Body.Close() 536 537 err = parseResponseJSON(response, &sessions) 538 return sessions, err 539 } 540 541 // SessionsByServiceType returns sessions from history filtered by type 542 func (client *Client) SessionsByServiceType(serviceType string) (contract.SessionListResponse, error) { 543 sessions, err := client.Sessions() 544 sessions = filterSessionsByType(serviceType, sessions) 545 return sessions, err 546 } 547 548 // SessionsByStatus returns sessions from history filtered by their status 549 func (client *Client) SessionsByStatus(status string) (contract.SessionListResponse, error) { 550 sessions, err := client.Sessions() 551 sessions = filterSessionsByStatus(status, sessions) 552 return sessions, err 553 } 554 555 // Services returns all running services 556 func (client *Client) Services() (services contract.ServiceListResponse, err error) { 557 response, err := client.http.Get("services", url.Values{}) 558 if err != nil { 559 return services, err 560 } 561 defer response.Body.Close() 562 563 err = parseResponseJSON(response, &services) 564 return services, err 565 } 566 567 // Service returns a service information by the requested id 568 func (client *Client) Service(id string) (service contract.ServiceInfoDTO, err error) { 569 response, err := client.http.Get("services/"+id, url.Values{}) 570 if err != nil { 571 return service, err 572 } 573 defer response.Body.Close() 574 575 err = parseResponseJSON(response, &service) 576 return service, err 577 } 578 579 // ServiceStart starts an instance of the service. 580 func (client *Client) ServiceStart(request contract.ServiceStartRequest) (service contract.ServiceInfoDTO, err error) { 581 response, err := client.http.Post("services", request) 582 if err != nil { 583 return service, err 584 } 585 defer response.Body.Close() 586 587 err = parseResponseJSON(response, &service) 588 return service, err 589 } 590 591 // ServiceStop stops the running service instance by the requested id. 592 func (client *Client) ServiceStop(id string) error { 593 path := fmt.Sprintf("services/%s", id) 594 response, err := client.http.Delete(path, nil) 595 if err != nil { 596 return err 597 } 598 defer response.Body.Close() 599 600 return nil 601 } 602 603 // NATStatus returns status of NAT traversal 604 func (client *Client) NATStatus() (status contract.NodeStatusResponse, err error) { 605 response, err := client.http.Get("node/monitoring-status", nil) 606 if err != nil { 607 return status, err 608 } 609 defer response.Body.Close() 610 611 err = parseResponseJSON(response, &status) 612 return status, err 613 } 614 615 // NATType returns type of NAT in sense of traversal capabilities 616 func (client *Client) NATType() (status contract.NATTypeDTO, err error) { 617 response, err := client.http.Get("nat/type", nil) 618 if err != nil { 619 return status, err 620 } 621 defer response.Body.Close() 622 623 err = parseResponseJSON(response, &status) 624 return status, err 625 } 626 627 // filterSessionsByType removes all sessions of irrelevant types 628 func filterSessionsByType(serviceType string, sessions contract.SessionListResponse) contract.SessionListResponse { 629 matches := 0 630 for _, s := range sessions.Items { 631 if s.ServiceType == serviceType { 632 sessions.Items[matches] = s 633 matches++ 634 } 635 } 636 sessions.Items = sessions.Items[:matches] 637 return sessions 638 } 639 640 // filterSessionsByStatus removes all sessions with non matching status 641 func filterSessionsByStatus(status string, sessions contract.SessionListResponse) contract.SessionListResponse { 642 matches := 0 643 for _, s := range sessions.Items { 644 if s.Status == status { 645 sessions.Items[matches] = s 646 matches++ 647 } 648 } 649 sessions.Items = sessions.Items[:matches] 650 return sessions 651 } 652 653 // Withdraw requests the withdrawal of money from l2 to l1 of hermes promises 654 func (client *Client) Withdraw(providerID identity.Identity, hermesID, beneficiary common.Address, amount *big.Int, fromChainID, toChainID int64) error { 655 withdrawRequest := contract.WithdrawRequest{ 656 ProviderID: providerID.Address, 657 HermesID: hermesID.Hex(), 658 Beneficiary: beneficiary.Hex(), 659 FromChainID: fromChainID, 660 ToChainID: toChainID, 661 } 662 663 if amount != nil { 664 withdrawRequest.Amount = amount.String() 665 } 666 667 path := "transactor/settle/withdraw" 668 669 response, err := client.http.Post(path, withdrawRequest) 670 if err != nil { 671 return err 672 } 673 defer response.Body.Close() 674 675 if response.StatusCode != http.StatusAccepted && response.StatusCode != http.StatusOK { 676 return errors.Wrap(err, "could not withdraw") 677 } 678 return nil 679 } 680 681 // Settle requests the settling of hermes promises 682 func (client *Client) Settle(providerID identity.Identity, hermesIDs []common.Address, waitForBlockchain bool) error { 683 settleRequest := contract.SettleRequest{ 684 ProviderID: providerID.Address, 685 HermesIDs: hermesIDs, 686 } 687 688 path := "transactor/settle/" 689 if waitForBlockchain { 690 path += "sync" 691 } else { 692 path += "async" 693 } 694 695 response, err := client.http.Post(path, settleRequest) 696 if err != nil { 697 return err 698 } 699 defer response.Body.Close() 700 701 if response.StatusCode != http.StatusAccepted && response.StatusCode != http.StatusOK { 702 return fmt.Errorf("could not settle promise") 703 } 704 return nil 705 } 706 707 // SettleIntoStake requests the settling of accountant promises into a stake increase 708 func (client *Client) SettleIntoStake(providerID, hermesID identity.Identity, waitForBlockchain bool) error { 709 settleRequest := contract.SettleRequest{ 710 ProviderID: providerID.Address, 711 HermesID: hermesID.Address, 712 } 713 714 path := "transactor/stake/increase/" 715 if waitForBlockchain { 716 path += "sync" 717 } else { 718 path += "async" 719 } 720 721 response, err := client.http.Post(path, settleRequest) 722 if err != nil { 723 return err 724 } 725 defer response.Body.Close() 726 727 if response.StatusCode != http.StatusAccepted && response.StatusCode != http.StatusOK { 728 return errors.Wrap(err, "could not settle promise") 729 } 730 return nil 731 } 732 733 // SettleWithBeneficiaryStatus set new beneficiary address for the provided identity. 734 func (client *Client) SettleWithBeneficiaryStatus(address string) (res contract.BeneficiaryTxStatus, err error) { 735 response, err := client.http.Get("identities/"+address+"/beneficiary-status", nil) 736 if err != nil { 737 return contract.BeneficiaryTxStatus{}, err 738 } 739 defer response.Body.Close() 740 741 if response.StatusCode != http.StatusOK { 742 return contract.BeneficiaryTxStatus{}, fmt.Errorf("expected 200 got %v", response.StatusCode) 743 } 744 745 err = parseResponseJSON(response, &res) 746 return res, err 747 } 748 749 // SettleWithBeneficiary set new beneficiary address for the provided identity. 750 func (client *Client) SettleWithBeneficiary(address, beneficiary, hermesID string) error { 751 payload := contract.SettleWithBeneficiaryRequest{ 752 ProviderID: address, 753 HermesID: hermesID, 754 Beneficiary: beneficiary, 755 } 756 response, err := client.http.Post("identities/"+address+"/beneficiary", payload) 757 if err != nil { 758 return err 759 } 760 defer response.Body.Close() 761 762 if response.StatusCode != http.StatusAccepted { 763 return fmt.Errorf("expected 202 got %v", response.StatusCode) 764 } 765 766 return nil 767 } 768 769 // DecreaseStake requests the decrease of stake via the transactor. 770 func (client *Client) DecreaseStake(ID identity.Identity, amount *big.Int) error { 771 decreaseRequest := contract.DecreaseStakeRequest{ 772 ID: ID.Address, 773 Amount: amount, 774 } 775 776 path := "transactor/stake/decrease" 777 778 response, err := client.http.Post(path, decreaseRequest) 779 if err != nil { 780 return err 781 } 782 defer response.Body.Close() 783 784 if response.StatusCode != http.StatusAccepted && response.StatusCode != http.StatusOK { 785 return errors.Wrap(err, "could not decrease stake") 786 } 787 return nil 788 } 789 790 // WithdrawalHistory returns latest withdrawals for identity 791 func (client *Client) WithdrawalHistory(address string) (res contract.SettlementListResponse, err error) { 792 params := url.Values{ 793 "types": []string{"withdrawal"}, 794 "provider_id": []string{address}, 795 } 796 797 path := fmt.Sprintf("transactor/settle/history?%s", params.Encode()) 798 response, err := client.http.Get(path, nil) 799 if err != nil { 800 return contract.SettlementListResponse{}, err 801 } 802 defer response.Body.Close() 803 804 err = parseResponseJSON(response, &res) 805 return res, err 806 } 807 808 // MigrateHermes migrate from old to active Hermes 809 func (client *Client) MigrateHermes(address string) error { 810 response, err := client.http.Post(fmt.Sprintf("identities/%s/migrate-hermes", address), nil) 811 if err != nil { 812 return err 813 } 814 defer response.Body.Close() 815 816 if response.StatusCode != http.StatusOK { 817 body, err := io.ReadAll(response.Body) 818 if err != nil { 819 return err 820 } 821 return fmt.Errorf("migration error: %s", body) 822 } 823 824 return nil 825 } 826 827 // MigrateHermesStatus check status of the migration 828 func (client *Client) MigrateHermesStatus(address string) (contract.MigrationStatusResponse, error) { 829 var res contract.MigrationStatusResponse 830 831 response, err := client.http.Get(fmt.Sprintf("identities/%s/migrate-hermes/status", address), nil) 832 if err != nil { 833 return res, err 834 } 835 defer response.Body.Close() 836 837 err = parseResponseJSON(response, &res) 838 839 return res, err 840 } 841 842 // Beneficiary gets beneficiary address for the provided identity. 843 func (client *Client) Beneficiary(address string) (res contract.IdentityBeneficiaryResponse, err error) { 844 response, err := client.http.Get("identities/"+address+"/beneficiary", nil) 845 if err != nil { 846 return contract.IdentityBeneficiaryResponse{}, err 847 } 848 defer response.Body.Close() 849 850 err = parseResponseJSON(response, &res) 851 return res, err 852 } 853 854 // SetMMNApiKey sets MMN's API key in config and registers node to MMN 855 func (client *Client) SetMMNApiKey(data contract.MMNApiKeyRequest) error { 856 response, err := client.http.Post("mmn/api-key", data) 857 // non 200 status codes return a generic error and we can't use it, instead 858 // the response contains validation JSON which we can use to extract the error 859 if err != nil { 860 return err 861 } 862 defer response.Body.Close() 863 864 if response.StatusCode != 200 { 865 return apierror.Parse(response) 866 } 867 868 return nil 869 } 870 871 // IdentityReferralCode returns a referral token for the given identity. 872 func (client *Client) IdentityReferralCode(identity string) (contract.ReferralTokenResponse, error) { 873 response, err := client.http.Get(fmt.Sprintf("identities/%v/referral", identity), nil) 874 if err != nil { 875 return contract.ReferralTokenResponse{}, err 876 } 877 defer response.Body.Close() 878 879 res := contract.ReferralTokenResponse{} 880 err = parseResponseJSON(response, &res) 881 return res, err 882 } 883 884 // OrderCreate creates a new order for currency exchange in pilvytis 885 func (client *Client) OrderCreate(id identity.Identity, gw string, order contract.PaymentOrderRequest) (contract.PaymentOrderResponse, error) { 886 resp, err := client.http.Post(fmt.Sprintf("v2/identities/%s/%s/payment-order", id.Address, gw), order) 887 if err != nil { 888 return contract.PaymentOrderResponse{}, err 889 } 890 defer resp.Body.Close() 891 892 var res contract.PaymentOrderResponse 893 return res, parseResponseJSON(resp, &res) 894 } 895 896 // OrderGet returns a single order istance given it's ID. 897 func (client *Client) OrderGet(address identity.Identity, orderID string) (contract.PaymentOrderResponse, error) { 898 path := fmt.Sprintf("v2/identities/%s/payment-order/%s", address.Address, orderID) 899 resp, err := client.http.Get(path, nil) 900 if err != nil { 901 return contract.PaymentOrderResponse{}, err 902 } 903 defer resp.Body.Close() 904 905 var res contract.PaymentOrderResponse 906 return res, parseResponseJSON(resp, &res) 907 } 908 909 // OrderGetAll returns all order istances for a given identity 910 func (client *Client) OrderGetAll(id identity.Identity) ([]contract.PaymentOrderResponse, error) { 911 path := fmt.Sprintf("v2/identities/%s/payment-order", id.Address) 912 resp, err := client.http.Get(path, nil) 913 if err != nil { 914 return nil, err 915 } 916 defer resp.Body.Close() 917 918 var res []contract.PaymentOrderResponse 919 return res, parseResponseJSON(resp, &res) 920 } 921 922 // OrderInvoice returns a single order istance given it's ID. 923 func (client *Client) OrderInvoice(address identity.Identity, orderID string) ([]byte, error) { 924 path := fmt.Sprintf("v2/identities/%s/payment-order/%s/invoice", address.Address, orderID) 925 resp, err := client.http.Get(path, nil) 926 if err != nil { 927 return nil, err 928 } 929 defer resp.Body.Close() 930 931 return io.ReadAll(resp.Body) 932 } 933 934 // PaymentOrderGateways returns all possible gateways and their data. 935 func (client *Client) PaymentOrderGateways(optionsCurrency exchange.Currency) ([]contract.GatewaysResponse, error) { 936 query := url.Values{} 937 query.Set("options_currency", string(optionsCurrency)) 938 resp, err := client.http.Get("v2/payment-order-gateways", query) 939 if err != nil { 940 return nil, err 941 } 942 defer resp.Body.Close() 943 944 var res []contract.GatewaysResponse 945 return res, parseResponseJSON(resp, &res) 946 } 947 948 // UpdateTerms takes a TermsRequest and sends it as an update 949 // for the terms of use. 950 func (client *Client) UpdateTerms(obj contract.TermsRequest) error { 951 resp, err := client.http.Post("terms", obj) 952 if err != nil { 953 return err 954 } 955 defer resp.Body.Close() 956 return nil 957 } 958 959 // FetchConfig - fetches current config 960 func (client *Client) FetchConfig() (map[string]interface{}, error) { 961 resp, err := client.http.Get("config", nil) 962 if err != nil { 963 return nil, err 964 } 965 defer resp.Body.Close() 966 967 if resp.StatusCode < 200 || resp.StatusCode > 299 { 968 return nil, fmt.Errorf("fetching config failed with status: %d", resp.StatusCode) 969 } 970 971 var res map[string]interface{} 972 err = parseResponseJSON(resp, &res) 973 if err != nil { 974 return nil, err 975 } 976 977 data, ok := res["data"] 978 if !ok { 979 return nil, errors.New("no field named 'data' found in config") 980 } 981 982 config := data.(map[string]interface{}) 983 return config, err 984 } 985 986 // SetConfig - set user config. 987 func (client *Client) SetConfig(data map[string]interface{}) error { 988 req := struct { 989 Data map[string]interface{} `json:"data"` 990 }{ 991 Data: data, 992 } 993 resp, err := client.http.Post("config/user", req) 994 if err != nil { 995 return err 996 } 997 998 defer resp.Body.Close() 999 1000 if resp.StatusCode < 200 || resp.StatusCode > 299 { 1001 return fmt.Errorf("failed to set user config with status: %d", resp.StatusCode) 1002 } 1003 1004 return nil 1005 }