github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/session/pingpong/hermes_caller.go (about) 1 /* 2 * Copyright (C) 2019 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 pingpong 19 20 import ( 21 "context" 22 "encoding/hex" 23 "encoding/json" 24 "errors" 25 "fmt" 26 "io" 27 "math/big" 28 "net/http" 29 "strings" 30 "sync" 31 "time" 32 33 "github.com/cenkalti/backoff/v4" 34 "github.com/ethereum/go-ethereum/common" 35 36 "github.com/mysteriumnetwork/node/identity" 37 "github.com/mysteriumnetwork/node/requests" 38 "github.com/mysteriumnetwork/payments/crypto" 39 ) 40 41 // HermesErrorResponse represents the errors that hermes returns 42 type HermesErrorResponse struct { 43 CausedBy string `json:"cause"` 44 ErrorMessage string `json:"message"` 45 ErrorData string `json:"data"` 46 c error 47 } 48 49 // Error returns the associated error 50 func (aer HermesErrorResponse) Error() string { 51 return aer.c.Error() 52 } 53 54 // Cause returns the associated cause 55 func (aer HermesErrorResponse) Cause() error { 56 return aer.c 57 } 58 59 // Unwrap unwraps the associated error 60 func (aer HermesErrorResponse) Unwrap() error { 61 return aer.c 62 } 63 64 // Data returns the associated data 65 func (aer HermesErrorResponse) Data() string { 66 return aer.ErrorData 67 } 68 69 // UnmarshalJSON unmarshals given data to HermesErrorResponse 70 func (aer *HermesErrorResponse) UnmarshalJSON(data []byte) error { 71 var s struct { 72 CausedBy string `json:"cause"` 73 ErrorMessage string `json:"message"` 74 ErrorData string `json:"data"` 75 } 76 77 err := json.Unmarshal(data, &s) 78 if err != nil { 79 return fmt.Errorf("could not unmarshal error data %w", err) 80 } 81 82 aer.CausedBy = s.CausedBy 83 aer.ErrorMessage = s.ErrorMessage 84 aer.ErrorData = s.ErrorData 85 86 if v, ok := hermesCauseToError[s.CausedBy]; ok { 87 aer.c = v 88 return nil 89 } 90 91 return fmt.Errorf("received unknown error: %v", s.CausedBy) 92 } 93 94 type hermesError interface { 95 Error() string 96 Cause() error 97 Data() string 98 } 99 100 // HermesCaller represents the http caller for hermes. 101 type HermesCaller struct { 102 transport *requests.HTTPClient 103 hermesBaseURI string 104 cache hermesCallerCache 105 } 106 107 // hermesCallerCache represents the cache for call responses 108 type hermesCallerCache struct { 109 data map[string]hermesCallerCacheData 110 lock sync.RWMutex 111 } 112 113 // hermesCallerCacheData represents the cache data 114 type hermesCallerCacheData struct { 115 info *HermesUserInfo 116 err error 117 updatedAt time.Time 118 } 119 120 // NewHermesCaller returns a new instance of hermes caller. 121 func NewHermesCaller(transport *requests.HTTPClient, hermesBaseURI string) *HermesCaller { 122 return &HermesCaller{ 123 transport: transport, 124 hermesBaseURI: hermesBaseURI, 125 cache: hermesCallerCache{ 126 data: make(map[string]hermesCallerCacheData), 127 }, 128 } 129 } 130 131 // RequestPromise represents the request for a new hermes promise 132 type RequestPromise struct { 133 ExchangeMessage crypto.ExchangeMessage `json:"exchange_message"` 134 TransactorFee *big.Int `json:"transactor_fee"` 135 RRecoveryData string `json:"r_recovery_data"` 136 } 137 138 // RequestPromise requests a promise from hermes. 139 func (ac *HermesCaller) RequestPromise(rp RequestPromise) (crypto.Promise, error) { 140 return ac.promiseRequest(rp, "request_promise") 141 } 142 143 func (ac *HermesCaller) promiseRequest(rp RequestPromise, endpoint string) (crypto.Promise, error) { 144 eback := backoff.NewConstantBackOff(time.Millisecond * 500) 145 boff := backoff.WithMaxRetries(eback, 3) 146 ctx, cancel := context.WithCancel(context.Background()) 147 defer cancel() 148 boff = backoff.WithContext(boff, ctx) 149 150 res := crypto.Promise{} 151 152 return res, backoff.Retry(func() error { 153 req, err := requests.NewPostRequest(ac.hermesBaseURI, endpoint, rp) 154 if err != nil { 155 cancel() 156 return fmt.Errorf("could not form %v request: %w", endpoint, err) 157 } 158 159 err = ac.doRequest(req, &res) 160 if err != nil { 161 // if too many requests, retry 162 if errors.Is(err, ErrTooManyRequests) { 163 return err 164 } 165 // otherwise, do not retry anymore and return the error 166 cancel() 167 return fmt.Errorf("could not request promise: %w", err) 168 } 169 return nil 170 }, boff) 171 } 172 173 // PayAndSettle requests a promise from hermes. 174 func (ac *HermesCaller) PayAndSettle(rp RequestPromise) (crypto.Promise, error) { 175 return ac.promiseRequest(rp, "pay_and_settle") 176 } 177 178 // SetPromiseFeeRequest represents the payload for changing a promise fee. 179 type SetPromiseFeeRequest struct { 180 HermesPromise crypto.Promise `json:"hermes_promise"` 181 NewFee *big.Int `json:"new_fee"` 182 } 183 184 // UpdatePromiseFee calls hermes to update its promise with new fee. 185 func (ac *HermesCaller) UpdatePromiseFee(promise crypto.Promise, newFee *big.Int) (crypto.Promise, error) { 186 req, err := requests.NewPostRequest(ac.hermesBaseURI, "change_promise_fee", SetPromiseFeeRequest{ 187 HermesPromise: promise, 188 NewFee: newFee, 189 }) 190 if err != nil { 191 return crypto.Promise{}, fmt.Errorf("could not form change promise fee request: %w", err) 192 } 193 194 res := crypto.Promise{} 195 return res, ac.doRequest(req, &res) 196 } 197 198 // RevealObject represents the reveal request object. 199 type RevealObject struct { 200 R string 201 Provider string 202 AgreementID *big.Int 203 } 204 205 // RevealR reveals hashlock key 'r' from 'provider' to the hermes for the agreement identified by 'agreementID'. 206 func (ac *HermesCaller) RevealR(r, provider string, agreementID *big.Int) error { 207 eback := backoff.NewConstantBackOff(time.Millisecond * 500) 208 boff := backoff.WithMaxRetries(eback, 3) 209 ctx, cancel := context.WithCancel(context.Background()) 210 defer cancel() 211 boff = backoff.WithContext(boff, ctx) 212 return backoff.Retry(func() error { 213 req, err := requests.NewPostRequest(ac.hermesBaseURI, "reveal_r", RevealObject{ 214 R: r, 215 Provider: provider, 216 AgreementID: agreementID, 217 }) 218 if err != nil { 219 cancel() 220 return fmt.Errorf("could not form reveal_r request: %w", err) 221 } 222 223 err = ac.doRequest(req, &RevealSuccess{}) 224 if err != nil { 225 // if too many requests, retry 226 if errors.Is(err, ErrTooManyRequests) { 227 return err 228 } 229 // otherwise, do not retry anymore and return the error 230 cancel() 231 return fmt.Errorf("could not reveal R for hermes: %w", err) 232 } 233 return nil 234 }, boff) 235 } 236 237 // IsIdentityOffchain returns true if identity is considered offchain in hermes. 238 func (ac *HermesCaller) IsIdentityOffchain(chainID int64, id string) (bool, error) { 239 data, err := ac.GetConsumerData(chainID, id, time.Second) 240 if err != nil { 241 if errors.Is(err, ErrHermesNotFound) { 242 return false, nil 243 } 244 return false, err 245 } 246 247 return data.IsOffchain, nil 248 } 249 250 type syncPromiseRequest struct { 251 ChannelID string `json:"channel_id"` 252 ChainID int64 `json:"chain_id"` 253 Amount *big.Int `json:"amount"` 254 Fee *big.Int `json:"fee"` 255 Hashlock string `json:"hashlock"` 256 Signature string `json:"signature"` 257 } 258 259 // SyncProviderPromise syncs provider promise. 260 func (ac *HermesCaller) SyncProviderPromise(promise crypto.Promise, signer identity.Signer) error { 261 toSend := syncPromiseRequest{ 262 ChannelID: common.Bytes2Hex(promise.ChannelID), 263 ChainID: promise.ChainID, 264 Amount: promise.Amount, 265 Fee: promise.Fee, 266 Hashlock: common.Bytes2Hex(promise.Hashlock), 267 Signature: common.Bytes2Hex(promise.Signature), 268 } 269 270 req, err := requests.NewSignedPostRequest(ac.hermesBaseURI, "provider/sync_promise", toSend, signer) 271 if err != nil { 272 return fmt.Errorf("could not make promise sync request: %w", err) 273 } 274 275 err = ac.doRequest(req, map[string]any{}) 276 if err != nil { 277 return fmt.Errorf("could not sync promise hermes: %w", err) 278 } 279 280 return nil 281 } 282 283 type refreshPromiseRequest struct { 284 ChainID int64 `json:"chain_id"` 285 Identity string `json:"identity"` 286 Hashlock string `json:"hashlock"` 287 RRecoveryData string `json:"r_recovery_data"` 288 } 289 290 // RefreshLatestProviderPromise reissue latest promise with a new hashlock. 291 func (ac *HermesCaller) RefreshLatestProviderPromise(chainID int64, id string, hashlock, recoveryData []byte, signer identity.Signer) (crypto.Promise, error) { 292 res := crypto.Promise{} 293 toSend := refreshPromiseRequest{ 294 ChainID: chainID, 295 Identity: id, 296 Hashlock: common.Bytes2Hex(hashlock), 297 RRecoveryData: common.Bytes2Hex(recoveryData), 298 } 299 300 req, err := requests.NewSignedPostRequest(ac.hermesBaseURI, "refresh_promise", toSend, signer) 301 if err != nil { 302 return res, fmt.Errorf("could not make promise sync request: %w", err) 303 } 304 305 err = ac.doRequest(req, &res) 306 if err != nil { 307 return res, fmt.Errorf("could not refresh promise: %w", err) 308 } 309 310 return res, nil 311 } 312 313 // GetConsumerData gets consumer data from hermes, use a negative cacheTime to force update 314 func (ac *HermesCaller) GetConsumerData(chainID int64, id string, cacheTime time.Duration) (HermesUserInfo, error) { 315 if cacheTime > 0 { 316 cachedResponse, cachedError, ok := ac.getResponseFromCache(chainID, id, cacheTime) 317 if ok { 318 return cachedResponse, cachedError 319 } 320 } 321 req, err := requests.NewGetRequest(ac.hermesBaseURI, fmt.Sprintf("data/consumer/%v", id), nil) 322 if err != nil { 323 return HermesUserInfo{}, fmt.Errorf("could not form consumer data request: %w", err) 324 } 325 var resp map[int64]HermesUserInfo 326 err = ac.doRequest(req, &resp) 327 if err != nil { 328 if errors.Is(err, ErrHermesNotFound) { 329 // also save not found status 330 ac.setCacheData(chainID, id, nil, err) 331 } 332 return HermesUserInfo{}, fmt.Errorf("could not request consumer data from hermes: %w", err) 333 } 334 335 data, ok := resp[chainID] 336 if !ok { 337 return HermesUserInfo{}, fmt.Errorf("could not get data for chain ID: %d", chainID) 338 } 339 340 err = data.LatestPromise.isValid(id) 341 if err != nil { 342 return HermesUserInfo{}, fmt.Errorf("could not check promise validity: %w", err) 343 } 344 345 ac.setCacheData(chainID, id, &data, nil) 346 347 return data, nil 348 } 349 350 func (ac *HermesCaller) setCacheData(chainID int64, id string, data *HermesUserInfo, err error) { 351 ac.cache.lock.Lock() 352 defer ac.cache.lock.Unlock() 353 354 ac.cache.data[getCacheKey(chainID, id)] = hermesCallerCacheData{ 355 updatedAt: time.Now(), 356 info: data, 357 err: err, 358 } 359 } 360 361 // GetProviderData gets provider data from hermes 362 func (ac *HermesCaller) GetProviderData(chainID int64, id string) (HermesUserInfo, error) { 363 return ac.getProviderData(chainID, id) 364 } 365 366 // ProviderPromiseAmountUnsafe returns the provider promise amount. 367 // If can also return `nil` as the result if no promise exists. 368 func (ac *HermesCaller) ProviderPromiseAmountUnsafe(chainID int64, id string) (*big.Int, error) { 369 d, err := ac.getProviderData(chainID, id) 370 if err != nil { 371 if errors.Is(err, ErrHermesNotFound) { 372 return nil, nil 373 } 374 375 return nil, err 376 } 377 378 return d.LatestPromise.Amount, nil 379 } 380 381 func (ac *HermesCaller) getProviderData(chainID int64, id string) (HermesUserInfo, error) { 382 req, err := requests.NewGetRequest(ac.hermesBaseURI, fmt.Sprintf("data/provider/%v", id), nil) 383 if err != nil { 384 return HermesUserInfo{}, fmt.Errorf("could not form consumer data request: %w", err) 385 } 386 var resp map[int64]HermesUserInfo 387 err = ac.doRequest(req, &resp) 388 if err != nil { 389 return HermesUserInfo{}, fmt.Errorf("could not request consumer data from hermes: %w", err) 390 } 391 392 data, ok := resp[chainID] 393 if !ok { 394 return HermesUserInfo{}, fmt.Errorf("could not get data for chain ID: %d", chainID) 395 } 396 397 return data, nil 398 } 399 400 func (ac *HermesCaller) doRequest(req *http.Request, to any) error { 401 resp, err := ac.transport.Do(req) 402 if err != nil { 403 return fmt.Errorf("could not execute request: %w", err) 404 } 405 defer resp.Body.Close() 406 body, err := io.ReadAll(resp.Body) 407 if err != nil { 408 return fmt.Errorf("could not read response body: %w", err) 409 } 410 411 if resp.StatusCode >= 200 && resp.StatusCode <= 300 { 412 // parse response 413 err = json.Unmarshal(body, &to) 414 if err != nil { 415 return fmt.Errorf("could not unmarshal response body: %w", err) 416 } 417 return nil 418 } 419 420 // parse error body 421 hermesError := HermesErrorResponse{} 422 if string(body) == "" { 423 hermesError.ErrorMessage = "Unknown error" 424 return hermesError 425 } 426 427 err = json.Unmarshal(body, &hermesError) 428 if err != nil { 429 return fmt.Errorf("could not unmarshal error body: %w", err) 430 } 431 432 return hermesError 433 } 434 435 func (ac *HermesCaller) getResponseFromCache(chainID int64, identity string, cacheDuration time.Duration) (HermesUserInfo, error, bool) { 436 ac.cache.lock.RLock() 437 defer ac.cache.lock.RUnlock() 438 439 cacheKey := getCacheKey(chainID, identity) 440 cachedResponse, ok := ac.cache.data[cacheKey] 441 if ok && cachedResponse.updatedAt.Add(cacheDuration).After(time.Now()) { 442 if cachedResponse.err != nil { 443 return HermesUserInfo{}, cachedResponse.err, true 444 } 445 return *cachedResponse.info, nil, true 446 } 447 return HermesUserInfo{}, nil, false 448 } 449 450 // HermesUserInfo represents the consumer data 451 type HermesUserInfo struct { 452 Identity string `json:"Identity"` 453 Beneficiary string `json:"Beneficiary"` 454 ChannelID string `json:"ChannelID"` 455 Balance *big.Int `json:"Balance"` 456 Settled *big.Int `json:"Settled"` 457 Stake *big.Int `json:"Stake"` 458 LatestPromise LatestPromise `json:"LatestPromise"` 459 LatestSettlement time.Time `json:"LatestSettlement"` 460 IsOffchain bool `json:"IsOffchain"` 461 } 462 463 func (cd *HermesUserInfo) fillZerosIfBigIntNull() *HermesUserInfo { 464 if cd.Balance == nil { 465 cd.Balance = big.NewInt(0) 466 } 467 468 if cd.Settled == nil { 469 cd.Settled = big.NewInt(0) 470 } 471 472 if cd.Stake == nil { 473 cd.Stake = big.NewInt(0) 474 } 475 476 if cd.LatestPromise.Amount == nil { 477 cd.LatestPromise.Amount = big.NewInt(0) 478 } 479 480 if cd.LatestPromise.Fee == nil { 481 cd.LatestPromise.Fee = big.NewInt(0) 482 } 483 484 return cd 485 } 486 487 // LatestPromise represents the latest promise 488 type LatestPromise struct { 489 ChainID int64 `json:"ChainID"` 490 ChannelID string `json:"ChannelID"` 491 Amount *big.Int `json:"Amount"` 492 Fee *big.Int `json:"Fee"` 493 Hashlock string `json:"Hashlock"` 494 Signature string `json:"Signature"` 495 } 496 497 // isValid checks if the promise is really issued by the given identity 498 func (lp LatestPromise) isValid(id string) error { 499 // if we've not promised anything, that's fine for us. 500 // handles the case when we've just registered the identity. 501 if lp.Amount == nil || lp.Amount.Cmp(new(big.Int)) == 0 { 502 return nil 503 } 504 505 decodedChannelID, err := hex.DecodeString(strings.TrimPrefix(lp.ChannelID, "0x")) 506 if err != nil { 507 return fmt.Errorf("could not decode channel ID: %w", err) 508 } 509 decodedHashlock, err := hex.DecodeString(strings.TrimPrefix(lp.Hashlock, "0x")) 510 if err != nil { 511 return fmt.Errorf("could not decode hashlock: %w", err) 512 } 513 decodedSignature, err := hex.DecodeString(strings.TrimPrefix(lp.Signature, "0x")) 514 if err != nil { 515 return fmt.Errorf("could not decode signature: %w", err) 516 } 517 518 p := crypto.Promise{ 519 ChainID: lp.ChainID, 520 ChannelID: decodedChannelID, 521 Amount: lp.Amount, 522 Fee: lp.Fee, 523 Hashlock: decodedHashlock, 524 Signature: decodedSignature, 525 } 526 527 if !p.IsPromiseValid(common.HexToAddress(id)) { 528 return fmt.Errorf("promise issued by wrong identity. Expected %q", id) 529 } 530 531 return nil 532 } 533 534 func getCacheKey(chainID int64, identity string) string { 535 return fmt.Sprintf("%d:%s", chainID, strings.ToLower(identity)) 536 } 537 538 // RevealSuccess represents the reveal success response from hermes 539 type RevealSuccess struct { 540 Message string `json:"message"` 541 } 542 543 // ErrHermesInvalidSignature indicates that an invalid signature was sent. 544 var ErrHermesInvalidSignature = errors.New("invalid signature") 545 546 // ErrHermesInternal represents an internal error. 547 var ErrHermesInternal = errors.New("internal error") 548 549 // ErrHermesPreviousRNotRevealed represents that a previous R has not been revealed yet. No actions will be possible before the R is revealed. 550 var ErrHermesPreviousRNotRevealed = errors.New("previous R not revealed") 551 552 // ErrHermesPaymentValueTooLow indicates that the agreement total has decreased as opposed to increasing. 553 var ErrHermesPaymentValueTooLow = errors.New("payment value too low") 554 555 // ErrHermesProviderBalanceExhausted indicates that the provider has run out of stake and a rebalance is needed. 556 var ErrHermesProviderBalanceExhausted = errors.New("provider balance exhausted, please rebalance your channel") 557 558 // ErrHermesPromiseValueTooLow represents an error where the consumer sent a promise with a decreasing total. 559 var ErrHermesPromiseValueTooLow = errors.New("promise value too low") 560 561 // ErrHermesOverspend indicates that the consumer has overspent his balance. 562 var ErrHermesOverspend = errors.New("consumer does not have enough balance and is overspending") 563 564 // ErrHermesMalformedJSON indicates that the provider has sent an invalid json in the request. 565 var ErrHermesMalformedJSON = errors.New("malformed json") 566 567 // ErrNeedsRRecovery indicates that we need to recover R. 568 var ErrNeedsRRecovery = errors.New("r recovery required") 569 570 // ErrInvalidPreviuosLatestPromise represents an error where historical promise data is invalid resulting in a non functional provider or consumner. 571 var ErrInvalidPreviuosLatestPromise = errors.New("invalid previuos latest promise, impossible to issue new one") 572 573 // ErrHermesNoPreviousPromise indicates that we have no previous knowledge of a promise for the provider. 574 var ErrHermesNoPreviousPromise = errors.New("no previous promise found") 575 576 // ErrHermesHashlockMissmatch occurs when an expected hashlock does not match the one sent by provider. 577 var ErrHermesHashlockMissmatch = errors.New("hashlock missmatch") 578 579 // ErrHermesNotFound occurs when a requested resource is not found 580 var ErrHermesNotFound = errors.New("resource not found") 581 582 // ErrTooManyRequests occurs when we call the reveal R or request promise errors asynchronously at the same time. 583 var ErrTooManyRequests = errors.New("too many simultaneous requests") 584 585 // ErrConsumerUnregistered indicates that the consumer is not registered. 586 var ErrConsumerUnregistered = errors.New("consumer unregistered") 587 588 var hermesCauseToError = map[string]error{ 589 ErrHermesInvalidSignature.Error(): ErrHermesInvalidSignature, 590 ErrHermesInternal.Error(): ErrHermesInternal, 591 ErrHermesPreviousRNotRevealed.Error(): ErrHermesPreviousRNotRevealed, 592 ErrHermesPaymentValueTooLow.Error(): ErrHermesPaymentValueTooLow, 593 ErrHermesProviderBalanceExhausted.Error(): ErrHermesProviderBalanceExhausted, 594 ErrHermesPromiseValueTooLow.Error(): ErrHermesPromiseValueTooLow, 595 ErrHermesOverspend.Error(): ErrHermesOverspend, 596 ErrHermesMalformedJSON.Error(): ErrHermesMalformedJSON, 597 ErrHermesNoPreviousPromise.Error(): ErrHermesNoPreviousPromise, 598 ErrHermesHashlockMissmatch.Error(): ErrHermesHashlockMissmatch, 599 ErrHermesNotFound.Error(): ErrHermesNotFound, 600 ErrNeedsRRecovery.Error(): ErrNeedsRRecovery, 601 ErrTooManyRequests.Error(): ErrTooManyRequests, 602 ErrConsumerUnregistered.Error(): ErrConsumerUnregistered, 603 ErrInvalidPreviuosLatestPromise.Error(): ErrInvalidPreviuosLatestPromise, 604 } 605 606 type rRecoveryDetails struct { 607 R string `json:"r"` 608 AgreementID *big.Int `json:"agreement_id"` 609 }