github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/tequilapi/endpoints/identities_test.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 "bytes" 22 "fmt" 23 "math/big" 24 "net/http" 25 "net/http/httptest" 26 "testing" 27 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/gin-gonic/gin" 30 "github.com/mysteriumnetwork/go-rest/apierror" 31 "github.com/mysteriumnetwork/node/identity" 32 "github.com/mysteriumnetwork/node/identity/registry" 33 "github.com/mysteriumnetwork/node/session/pingpong" 34 pingpongEvent "github.com/mysteriumnetwork/node/session/pingpong/event" 35 "github.com/mysteriumnetwork/payments/client" 36 "github.com/stretchr/testify/assert" 37 ) 38 39 const identityUrl = "/irrelevant" 40 41 var ( 42 existingIdentities = []identity.Identity{ 43 {Address: "0x000000000000000000000000000000000000000a"}, 44 {Address: "0x000000000000000000000000000000000000beef"}, 45 } 46 newIdentity = identity.Identity{Address: "0x000000000000000000000000000000000000aaac"} 47 ) 48 49 type mockBeneficiaryProvider struct { 50 b common.Address 51 } 52 53 func (ms *mockBeneficiaryProvider) GetBeneficiary(identity common.Address) (common.Address, error) { 54 return ms.b, nil 55 } 56 57 type selectorFake struct { 58 } 59 60 func summonTestGin() *gin.Engine { 61 g := gin.Default() 62 g.Use(apierror.ErrorHandler) 63 return g 64 } 65 66 func (hf *selectorFake) UseOrCreate(address, _ string, _ int64) (identity.Identity, error) { 67 if len(address) > 0 { 68 return identity.Identity{Address: address}, nil 69 } 70 71 return identity.Identity{Address: "0x000000"}, nil 72 } 73 74 func (hf *selectorFake) SetDefault(address string) error { 75 return nil 76 } 77 78 func TestCurrentIdentitySuccess(t *testing.T) { 79 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 80 resp := httptest.NewRecorder() 81 req, err := http.NewRequest( 82 http.MethodPut, 83 "/identities/current", 84 bytes.NewBufferString(`{"passphrase": "mypassphrase"}`), 85 ) 86 assert.Nil(t, err) 87 88 endpoint := &identitiesAPI{ 89 idm: mockIdm, 90 selector: &selectorFake{}, 91 } 92 93 g := summonTestGin() 94 g.PUT("/identities/current", endpoint.Current) 95 96 g.ServeHTTP(resp, req) 97 98 assert.Equal(t, http.StatusOK, resp.Code) 99 assert.JSONEq( 100 t, 101 `{ 102 "id": "0x000000" 103 }`, 104 resp.Body.String(), 105 ) 106 } 107 108 func TestUnlockIdentitySuccess(t *testing.T) { 109 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 110 resp := httptest.NewRecorder() 111 req, err := http.NewRequest( 112 http.MethodPut, 113 fmt.Sprintf("/identities/%s/unlock", "0x000000000000000000000000000000000000000a"), 114 bytes.NewBufferString(`{"passphrase": "mypassphrase"}`), 115 ) 116 assert.Nil(t, err) 117 118 endpoint := &identitiesAPI{idm: mockIdm} 119 120 g := summonTestGin() 121 g.PUT("/identities/:id/unlock", endpoint.Unlock) 122 123 g.ServeHTTP(resp, req) 124 125 assert.Equal(t, http.StatusAccepted, resp.Code) 126 127 assert.Equal(t, "0x000000000000000000000000000000000000000a", mockIdm.LastUnlockAddress) 128 assert.Equal(t, "mypassphrase", mockIdm.LastUnlockPassphrase) 129 assert.Equal(t, int64(0), mockIdm.LastUnlockChainID) 130 } 131 132 func TestUnlockIdentityWithInvalidJSON(t *testing.T) { 133 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 134 resp := httptest.NewRecorder() 135 req, err := http.NewRequest( 136 http.MethodPut, 137 fmt.Sprintf("/identities/%s/unlock", "0x000000000000000000000000000000000000000a"), 138 bytes.NewBufferString(`{invalid json}`), 139 ) 140 assert.Nil(t, err) 141 142 endpoint := &identitiesAPI{idm: mockIdm} 143 g := summonTestGin() 144 g.PUT("/identities/:id/unlock", endpoint.Unlock) 145 146 g.ServeHTTP(resp, req) 147 148 assert.Equal(t, http.StatusBadRequest, resp.Code) 149 } 150 151 func TestUnlockIdentityWithNoPassphrase(t *testing.T) { 152 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 153 resp := httptest.NewRecorder() 154 req, err := http.NewRequest( 155 http.MethodPut, 156 fmt.Sprintf("/identities/%s/unlock", "0x000000000000000000000000000000000000000a"), 157 bytes.NewBufferString(`{}`), 158 ) 159 assert.NoError(t, err) 160 161 endpoint := &identitiesAPI{idm: mockIdm} 162 g := summonTestGin() 163 g.PUT("/identities/:id/unlock", endpoint.Unlock) 164 165 g.ServeHTTP(resp, req) 166 167 assert.Equal(t, http.StatusBadRequest, resp.Code) 168 assert.JSONEq( 169 t, 170 `{ 171 "error": { 172 "code": "validation_failed", 173 "message": "Request validation failed", 174 "detail": "Request validation failed: passphrase: 'passphrase' is required [required]", 175 "fields": { 176 "passphrase": { 177 "code": "required", 178 "message": "'passphrase' is required" 179 } 180 } 181 }, 182 "status": 400, 183 "path": "/identities/0x000000000000000000000000000000000000000a/unlock" 184 }`, 185 resp.Body.String(), 186 ) 187 } 188 189 func TestBeneficiaryWithChannel(t *testing.T) { 190 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 191 resp := httptest.NewRecorder() 192 req, err := http.NewRequest( 193 http.MethodGet, 194 fmt.Sprintf("/identities/%s/beneficiary", "0x000000000000000000000000000000000000000a"), 195 nil, 196 ) 197 assert.Nil(t, err) 198 199 endpoint := &identitiesAPI{ 200 idm: mockIdm, 201 addressProvider: &mockAddressProvider{ 202 hermesToReturn: common.HexToAddress("0x000000000000000000000000000000000000000b"), 203 registryToReturn: common.HexToAddress("0x00000000000000000000000000000000000000cb"), 204 channelToReturn: common.HexToAddress("0x0000000000000000000000000000000000000ddb"), 205 channelAddressToReturn: common.HexToAddress("0x0000000000000000000000000000000000001234"), 206 }, 207 bprovider: &mockBeneficiaryProvider{ 208 b: common.HexToAddress("0x0000000000000000000000000000000000001234"), 209 }, 210 } 211 g := summonTestGin() 212 g.GET("/identities/:id/beneficiary", endpoint.Beneficiary) 213 214 g.ServeHTTP(resp, req) 215 216 assert.Equal(t, http.StatusOK, resp.Code) 217 assert.JSONEq( 218 t, 219 `{ 220 "beneficiary":"0x0000000000000000000000000000000000001234", 221 "is_channel_address":true 222 }`, 223 resp.Body.String(), 224 ) 225 } 226 227 func TestBeneficiaryWithoutChannel(t *testing.T) { 228 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 229 resp := httptest.NewRecorder() 230 req, err := http.NewRequest( 231 http.MethodGet, 232 fmt.Sprintf("/identities/%s/beneficiary", "0x000000000000000000000000000000000000000a"), 233 nil, 234 ) 235 assert.Nil(t, err) 236 237 endpoint := &identitiesAPI{ 238 idm: mockIdm, 239 addressProvider: &mockAddressProvider{ 240 hermesToReturn: common.HexToAddress("0x000000000000000000000000000000000000000b"), 241 registryToReturn: common.HexToAddress("0x00000000000000000000000000000000000000cb"), 242 channelToReturn: common.HexToAddress("0x0000000000000000000000000000000000000ddb"), 243 channelAddressToReturn: common.HexToAddress("0x000000000000000000000000000000000000eeeb"), 244 }, 245 bprovider: &mockBeneficiaryProvider{ 246 b: common.HexToAddress("0x0000000000000000000000000000000000000123"), 247 }, 248 } 249 g := summonTestGin() 250 g.GET("/identities/:id/beneficiary", endpoint.Beneficiary) 251 252 g.ServeHTTP(resp, req) 253 254 assert.Equal(t, http.StatusOK, resp.Code) 255 assert.JSONEq( 256 t, 257 `{ 258 "beneficiary":"0x0000000000000000000000000000000000000123", 259 "is_channel_address":false 260 }`, 261 resp.Body.String(), 262 ) 263 } 264 265 func TestUnlockFailure(t *testing.T) { 266 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 267 resp := httptest.NewRecorder() 268 req, err := http.NewRequest( 269 http.MethodPut, 270 fmt.Sprintf("/identities/%s/unlock", "0x000000000000000000000000000000000000000a"), 271 bytes.NewBufferString(`{"passphrase": "mypassphrase"}`), 272 ) 273 assert.Nil(t, err) 274 275 mockIdm.MarkUnlockToFail() 276 277 endpoint := &identitiesAPI{idm: mockIdm} 278 g := summonTestGin() 279 g.PUT("/identities/:id/unlock", endpoint.Unlock) 280 281 g.ServeHTTP(resp, req) 282 283 assert.Equal(t, http.StatusForbidden, resp.Code) 284 285 assert.Equal(t, "0x000000000000000000000000000000000000000a", mockIdm.LastUnlockAddress) 286 assert.Equal(t, "mypassphrase", mockIdm.LastUnlockPassphrase) 287 assert.Equal(t, int64(0), mockIdm.LastUnlockChainID) 288 } 289 290 func TestCreateNewIdentityEmptyPassphrase(t *testing.T) { 291 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 292 resp := httptest.NewRecorder() 293 req, err := http.NewRequest( 294 http.MethodPost, 295 "/identities", 296 bytes.NewBufferString(`{"passphrase": ""}`), 297 ) 298 assert.Nil(t, err) 299 300 endpoint := &identitiesAPI{idm: mockIdm} 301 g := summonTestGin() 302 g.POST("/identities", endpoint.Create) 303 304 g.ServeHTTP(resp, req) 305 306 assert.Equal(t, http.StatusOK, resp.Code) 307 } 308 309 func TestCreateNewIdentityNoPassphrase(t *testing.T) { 310 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 311 resp := httptest.NewRecorder() 312 req, err := http.NewRequest( 313 http.MethodPost, 314 "/identities", 315 bytes.NewBufferString(`{}`), 316 ) 317 assert.Nil(t, err) 318 319 endpoint := &identitiesAPI{idm: mockIdm} 320 g := summonTestGin() 321 g.POST("/identities", endpoint.Create) 322 323 g.ServeHTTP(resp, req) 324 325 fmt.Println(resp.Body.String()) 326 assert.Equal(t, http.StatusBadRequest, resp.Code) 327 assert.JSONEq( 328 t, 329 `{ 330 "error": { 331 "code": "validation_failed", 332 "message": "Request validation failed", 333 "detail": "Request validation failed: passphrase: 'passphrase' is required [required]", 334 "fields": { 335 "passphrase": { 336 "code": "required", 337 "message": "'passphrase' is required" 338 } 339 } 340 }, 341 "status": 400, 342 "path": "/identities" 343 }`, 344 resp.Body.String(), 345 ) 346 } 347 348 func TestCreateNewIdentity(t *testing.T) { 349 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 350 resp := httptest.NewRecorder() 351 req, err := http.NewRequest( 352 http.MethodPost, 353 "/identities", 354 bytes.NewBufferString(`{"passphrase": "mypass"}`), 355 ) 356 assert.Nil(t, err) 357 358 endpoint := &identitiesAPI{idm: mockIdm} 359 g := summonTestGin() 360 g.POST("/identities", endpoint.Create) 361 362 g.ServeHTTP(resp, req) 363 364 assert.JSONEq( 365 t, 366 `{ 367 "id": "0x000000000000000000000000000000000000aaac" 368 }`, 369 resp.Body.String(), 370 ) 371 } 372 373 func TestListIdentities(t *testing.T) { 374 mockIdm := identity.NewIdentityManagerFake(existingIdentities, newIdentity) 375 path := "/identities" 376 req := httptest.NewRequest("GET", path, nil) 377 resp := httptest.NewRecorder() 378 379 endpoint := &identitiesAPI{idm: mockIdm} 380 g := summonTestGin() 381 g.GET(path, endpoint.List) 382 383 g.ServeHTTP(resp, req) 384 385 assert.JSONEq( 386 t, 387 `{ 388 "identities": [ 389 {"id": "0x000000000000000000000000000000000000000a"}, 390 {"id": "0x000000000000000000000000000000000000beef"} 391 ] 392 }`, 393 resp.Body.String(), 394 ) 395 } 396 397 func Test_IdentityGet(t *testing.T) { 398 endpoint := &identitiesAPI{ 399 idm: identity.NewIdentityManagerFake(existingIdentities, newIdentity), 400 registry: ®istry.FakeRegistry{RegistrationStatus: registry.Registered}, 401 addressProvider: &mockAddressProvider{ 402 channelAddressToReturn: common.HexToAddress("0x100000000000000000000000000000000000000a"), 403 hermesToReturn: common.HexToAddress("0x200000000000000000000000000000000000000a"), 404 }, 405 bc: &mockProviderChannelStatusProvider{ 406 channelToReturn: client.ProviderChannel{ 407 Settled: big.NewInt(1), 408 Stake: big.NewInt(2), 409 LastUsedNonce: big.NewInt(3), 410 Timelock: big.NewInt(4), 411 }, 412 }, 413 earningsProvider: &mockEarningsProvider{ 414 earnings: pingpongEvent.EarningsDetailed{ 415 Total: pingpongEvent.Earnings{ 416 LifetimeBalance: big.NewInt(100), 417 UnsettledBalance: big.NewInt(50), 418 }, 419 PerHermes: map[common.Address]pingpongEvent.Earnings{ 420 common.HexToAddress("0x200000000000000000000000000000000000000a"): { 421 LifetimeBalance: big.NewInt(100), 422 UnsettledBalance: big.NewInt(50), 423 }, 424 }, 425 }, 426 }, 427 balanceProvider: &mockBalanceProvider{ 428 balance: big.NewInt(25), 429 }, 430 } 431 432 router := gin.Default() 433 router.GET("/identities/:id", endpoint.Get) 434 435 req, err := http.NewRequest( 436 http.MethodGet, 437 "/identities/0x000000000000000000000000000000000000000a", 438 nil, 439 ) 440 assert.Nil(t, err) 441 442 resp := httptest.NewRecorder() 443 router.ServeHTTP(resp, req) 444 assert.Equal(t, http.StatusOK, resp.Code) 445 446 assert.JSONEq(t, 447 ` 448 { 449 "id": "0x000000000000000000000000000000000000000a", 450 "registration_status": "Registered", 451 "channel_address": "0x100000000000000000000000000000000000000A", 452 "balance": 25, 453 "earnings": 50, 454 "earnings_total": 100, 455 "balance_tokens": { 456 "wei": "25", 457 "ether": "0.000000000000000025", 458 "human": "0" 459 }, 460 "earnings_tokens": { 461 "wei": "50", 462 "ether": "0.00000000000000005", 463 "human": "0" 464 }, 465 "earnings_total_tokens": { 466 "wei": "100", 467 "ether": "0.0000000000000001", 468 "human": "0" 469 }, 470 "stake": 2, 471 "hermes_id": "0x200000000000000000000000000000000000000A", 472 "earnings_per_hermes": { 473 "0x200000000000000000000000000000000000000A": { 474 "earnings": { 475 "wei": "50", 476 "ether": "0.00000000000000005", 477 "human": "0" 478 }, 479 "earnings_total": { 480 "wei": "100", 481 "ether": "0.0000000000000001", 482 "human": "0" 483 } 484 } 485 } 486 } 487 `, 488 resp.Body.String()) 489 } 490 491 type mockAddressProvider struct { 492 hermesToReturn common.Address 493 registryToReturn common.Address 494 channelToReturn common.Address 495 channelAddressToReturn common.Address 496 } 497 498 func (ma *mockAddressProvider) GetActiveChannelImplementation(chainID int64) (common.Address, error) { 499 return ma.channelToReturn, nil 500 } 501 502 func (ma *mockAddressProvider) GetChannelImplementationForHermes(chainID int64, hermes common.Address) (common.Address, error) { 503 return ma.channelToReturn, nil 504 } 505 506 func (ma *mockAddressProvider) GetMystAddress(chainID int64) (common.Address, error) { 507 return ma.channelToReturn, nil 508 } 509 510 func (ma *mockAddressProvider) GetActiveHermes(chainID int64) (common.Address, error) { 511 return ma.hermesToReturn, nil 512 } 513 514 func (ma *mockAddressProvider) GetRegistryAddress(chainID int64) (common.Address, error) { 515 return ma.registryToReturn, nil 516 } 517 518 func (ma *mockAddressProvider) GetActiveChannelAddress(chainID int64, id common.Address) (common.Address, error) { 519 return ma.channelAddressToReturn, nil 520 } 521 522 func (ma *mockAddressProvider) GetHermesChannelAddress(chainID int64, id, hermes common.Address) (common.Address, error) { 523 return ma.channelAddressToReturn, nil 524 } 525 526 func (ma *mockAddressProvider) GetKnownHermeses(chainID int64) ([]common.Address, error) { 527 return []common.Address{ma.hermesToReturn}, nil 528 } 529 530 type mockProviderChannelStatusProvider struct { 531 channelToReturn client.ProviderChannel 532 } 533 534 func (m *mockProviderChannelStatusProvider) GetProviderChannel(chainID int64, hermesAddress common.Address, provider common.Address, pending bool) (client.ProviderChannel, error) { 535 return m.channelToReturn, nil 536 } 537 538 type mockEarningsProvider struct { 539 earnings pingpongEvent.EarningsDetailed 540 channels []pingpong.HermesChannel 541 } 542 543 func (mep *mockEarningsProvider) List(chainID int64) []pingpong.HermesChannel { 544 return mep.channels 545 } 546 547 func (mep *mockEarningsProvider) GetEarningsDetailed(chainID int64, _ identity.Identity) *pingpongEvent.EarningsDetailed { 548 return &mep.earnings 549 } 550 551 type mockBalanceProvider struct { 552 balance *big.Int 553 forceUpdateBalance *big.Int 554 } 555 556 func (m *mockBalanceProvider) GetBalance(chainID int64, id identity.Identity) *big.Int { 557 return m.balance 558 } 559 func (m *mockBalanceProvider) ForceBalanceUpdateCached(chainID int64, id identity.Identity) *big.Int { 560 return m.forceUpdateBalance 561 }