github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/e2e/connection_test.go (about) 1 /* 2 * Copyright (C) 2018 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package e2e 19 20 import ( 21 "context" 22 "fmt" 23 "math" 24 "math/big" 25 "sync" 26 "testing" 27 "time" 28 29 "github.com/stretchr/testify/require" 30 31 "github.com/ethereum/go-ethereum/accounts" 32 "github.com/ethereum/go-ethereum/accounts/abi/bind" 33 "github.com/ethereum/go-ethereum/accounts/keystore" 34 "github.com/ethereum/go-ethereum/common" 35 "github.com/ethereum/go-ethereum/core/types" 36 "github.com/ethereum/go-ethereum/ethclient" 37 "github.com/rs/zerolog/log" 38 "github.com/stretchr/testify/assert" 39 40 "github.com/mysteriumnetwork/node/identity" 41 "github.com/mysteriumnetwork/node/requests" 42 "github.com/mysteriumnetwork/node/session/pingpong" 43 tequilapi_client "github.com/mysteriumnetwork/node/tequilapi/client" 44 "github.com/mysteriumnetwork/node/tequilapi/contract" 45 "github.com/mysteriumnetwork/payments/bindings" 46 "github.com/mysteriumnetwork/payments/crypto" 47 ) 48 49 var ( 50 consumerPassphrase = "localconsumer" 51 providerID = "0xd1a23227bd5ad77f36ba62badcb78a410a1db6c5" 52 providerPassphrase = "localprovider" 53 chainID int64 = 80001 54 hermesID = "0xd68defb97d0765741f8ecf179df2f9564e1466a3" 55 hermes2ID = "0xfd63dc49c7163d82d6f0a4c23ff13216d702ce50" 56 mystAddress = "0xaa9c4e723609cb913430143fbc86d3cbe7adca21" 57 registryAddress = "0x427c2bad22335710aec5e477f3e3adcd313a9bcb" 58 channelImplementation = "0x599d43715df3070f83355d9d90ae62c159e62a75" 59 addressForTopups = "0xa29fb77b25181df094908b027821a7492ca4245b" 60 tenthThou = float64(1) / float64(10000) 61 ) 62 63 var ( 64 ethClient *ethclient.Client 65 ethClientL2 *ethclient.Client 66 ethSignerBuilder func(client *ethclient.Client) func(address common.Address, tx *types.Transaction) (*types.Transaction, error) 67 ) 68 69 var ( 70 providerChannelAddress = "0xa2305c7214045100B6EF5Df8f8FEc5C57F42051A" 71 balanceAfterRegistration, _ = big.NewInt(0).SetString("7000000000000000000", 10) 72 registrationFee, _ = big.NewInt(0).SetString("100000000000000000", 10) 73 ) 74 75 type consumer struct { 76 consumerID string 77 serviceType string 78 tequila func() *tequilapi_client.Client 79 balanceSpent *big.Int 80 proposal contract.ProposalDTO 81 composeService string 82 hermesID common.Address 83 hermesURL string 84 } 85 86 var consumersToTest = []*consumer{ 87 { 88 serviceType: "noop", 89 hermesID: common.HexToAddress(hermesID), 90 composeService: "myst-consumer-noop", 91 hermesURL: "http://hermes:8889/api/v2", 92 tequila: func() *tequilapi_client.Client { 93 return newTequilapiConsumer("myst-consumer-noop") 94 }, 95 }, 96 { 97 hermesID: common.HexToAddress(hermesID), 98 serviceType: "wireguard", 99 hermesURL: "http://hermes:8889/api/v2", 100 composeService: "myst-consumer-wireguard", 101 tequila: func() *tequilapi_client.Client { 102 return newTequilapiConsumer("myst-consumer-wireguard") 103 }, 104 }, 105 { 106 hermesID: common.HexToAddress(hermesID), 107 hermesURL: "http://hermes:8889/api/v2", 108 serviceType: "openvpn", 109 composeService: "myst-consumer-openvpn", 110 tequila: func() *tequilapi_client.Client { 111 return newTequilapiConsumer("myst-consumer-openvpn") 112 }, 113 }, 114 { 115 hermesURL: "http://hermes2:8889/api/v2", 116 hermesID: common.HexToAddress(hermes2ID), 117 serviceType: "wireguard", 118 composeService: "myst-consumer-hermes2", 119 tequila: func() *tequilapi_client.Client { 120 return newTequilapiConsumer("myst-consumer-hermes2") 121 }, 122 }, 123 } 124 125 func TestConsumerConnectsToProvider(t *testing.T) { 126 initEthClient(t) 127 128 tequilapiProvider := newTequilapiProvider() 129 t.Run("Provider has a registered identity", func(t *testing.T) { 130 providerRegistrationFlow(t, tequilapiProvider, providerID, providerPassphrase) 131 }) 132 133 t.Run("Consumer Creates And Registers Identity", func(t *testing.T) { 134 wg := sync.WaitGroup{} 135 wg.Add(len(consumersToTest)) 136 topUps := make(chan *consumer, len(consumersToTest)) 137 defer close(topUps) 138 139 go func() { 140 for c := range topUps { 141 fees, err := c.tequila().GetTransactorFees() 142 assert.NoError(t, err) 143 topUpConsumer(t, c.consumerID, c.hermesID, fees.Registration) 144 } 145 }() 146 147 for _, c := range consumersToTest { 148 go func(c *consumer) { 149 defer wg.Done() 150 tequilapiConsumer := c.tequila() 151 consumerID := identityCreateFlow(t, tequilapiConsumer, consumerPassphrase) 152 c.consumerID = consumerID 153 topUps <- c 154 consumerRegistrationFlow(t, tequilapiConsumer, consumerID, consumerPassphrase) 155 }(c) 156 } 157 158 wg.Wait() 159 }) 160 161 t.Run("Consumers Connect to provider", func(t *testing.T) { 162 wg := sync.WaitGroup{} 163 for _, c := range consumersToTest { 164 wg.Add(1) 165 go func(c *consumer) { 166 defer wg.Done() 167 proposal := consumerPicksProposal(t, c.tequila(), c.serviceType) 168 balanceSpent := consumerConnectFlow(t, c.tequila(), c.consumerID, c.hermesID.Hex(), c.serviceType, proposal) 169 c.balanceSpent = balanceSpent 170 c.proposal = proposal 171 recheckBalancesWithHermes(t, c.consumerID, balanceSpent, c.serviceType, c.hermesURL) 172 }(c) 173 } 174 175 wg.Wait() 176 }) 177 t.Run("Validate provider earnings", func(t *testing.T) { 178 sum := new(big.Int) 179 for _, v := range consumersToTest { 180 sum = new(big.Int).Add(sum, v.balanceSpent) 181 } 182 providerEarnedTokens(t, tequilapiProvider, providerID, sum) 183 }) 184 185 t.Run("Provider settlement flow", func(t *testing.T) { 186 caller, err := bindings.NewMystTokenCaller(common.HexToAddress(mystAddress), ethClient) 187 assert.NoError(t, err) 188 189 providerStatus, err := tequilapiProvider.Identity(providerID) 190 assert.NoError(t, err) 191 assert.Equal(t, balanceAfterRegistration, providerStatus.Balance) 192 193 // try to settle hermes 1 194 hermeses := []common.Address{ 195 common.HexToAddress(hermesID), 196 } 197 err = tequilapiProvider.Settle(identity.FromAddress(providerID), hermeses, false) 198 assert.Error(t, err) 199 assert.Contains(t, err.Error(), "is set as beneficiary, skip settling") 200 201 hermeses = append(hermeses, common.HexToAddress(hermes2ID)) 202 beneficiary := "0x1234aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa123412" 203 err = tequilapiProvider.SetBeneficiaryAsync(providerID, beneficiary) 204 assert.NoError(t, err) 205 206 // settle hermes 1 and 2 (should settle with beneficiary) 207 err = tequilapiProvider.Settle(identity.FromAddress(providerID), hermeses, true) 208 assert.NoError(t, err) 209 210 providerStatus, err = tequilapiProvider.Identity(providerID) 211 assert.NoError(t, err) 212 213 fees, err := tequilapiProvider.GetTransactorFees() 214 assert.NoError(t, err) 215 216 earningsByHermes := make(map[common.Address]*big.Int) 217 for _, c := range consumersToTest { 218 copy := earningsByHermes[c.hermesID] 219 220 if copy == nil { 221 copy = new(big.Int) 222 } 223 224 copy = new(big.Int).Add(copy, c.balanceSpent) 225 earningsByHermes[c.hermesID] = copy 226 } 227 hermesOneEarnings := earningsByHermes[common.HexToAddress(hermesID)] 228 hermesTwoEarnings := earningsByHermes[common.HexToAddress(hermes2ID)] 229 totalEarnings := new(big.Int).Add(hermesOneEarnings, hermesTwoEarnings) 230 fdiff := getDiffFloat(providerStatus.EarningsTotal, totalEarnings) 231 assert.True(t, fdiff < tenthThou) 232 233 hic, err := bindings.NewHermesImplementationCaller(common.HexToAddress(hermesID), ethClient) 234 assert.NoError(t, err) 235 236 hermesFee, err := hic.CalculateHermesFee(&bind.CallOpts{}, totalEarnings) 237 assert.NoError(t, err) 238 239 feeSum := big.NewInt(0).Add(big.NewInt(0).Add(fees.Settlement, hermesFee), fees.Settlement) 240 expected := new(big.Int).Sub(totalEarnings, feeSum) 241 balance, err := caller.BalanceOf(&bind.CallOpts{}, common.HexToAddress(beneficiary)) 242 assert.NoError(t, err) 243 244 diff := getDiffFloat(balance, expected) 245 diff = math.Abs(diff) 246 247 assert.True(t, diff >= 0 && diff <= tenthThou, fmt.Sprintf("got diff %v", diff)) 248 }) 249 250 t.Run("Provider withdraws to l1", func(t *testing.T) { 251 // since we've changed the benef, our channel is empty. Pretend that we do have myst in it. 252 chid, err := crypto.GenerateChannelAddress(providerID, hermes2ID, registryAddress, channelImplementation) 253 assert.NoError(t, err) 254 mintMyst(t, crypto.FloatToBigMyst(1), common.HexToAddress(chid), ethClientL2) 255 256 beneficiary := common.HexToAddress("0x1231adadadadaadada123123") 257 caller, err := bindings.NewMystTokenCaller(common.HexToAddress(mystAddress), ethClient) 258 assert.NoError(t, err) 259 260 balance, err := caller.BalanceOf(&bind.CallOpts{}, beneficiary) 261 assert.NoError(t, err) 262 assert.Equal(t, big.NewInt(0).Uint64(), balance.Uint64()) 263 264 err = tequilapiProvider.Withdraw(identity.FromAddress(providerID), common.HexToAddress(hermes2ID), beneficiary, nil, chainID, 0) 265 assert.NoError(t, err) 266 267 assert.Eventually(t, func() bool { 268 balance, _ := caller.BalanceOf(&bind.CallOpts{}, beneficiary) 269 return balance.Cmp(big.NewInt(0)) == 1 270 }, time.Second*20, time.Millisecond*100) 271 }) 272 273 t.Run("Provider stops services", func(t *testing.T) { 274 services, err := tequilapiProvider.Services() 275 assert.NoError(t, err) 276 277 for _, service := range services { 278 err := tequilapiProvider.ServiceStop(service.ID) 279 assert.NoError(t, err) 280 } 281 }) 282 283 t.Run("Provider starts whitelisted noop services", func(t *testing.T) { 284 req := contract.ServiceStartRequest{ 285 ProviderID: providerID, 286 Type: "noop", 287 AccessPolicies: &contract.ServiceAccessPolicies{IDs: []string{"mysterium"}}, 288 } 289 290 _, err := tequilapiProvider.ServiceStart(req) 291 assert.NoError(t, err) 292 }) 293 294 t.Run("Whitelisted consumer connects to the whitelisted noop service", func(t *testing.T) { 295 c := consumersToTest[0] 296 297 topUpConsumer(t, "0xc4cb9a91b8498776f6f8a0d5a2a23beec9b3cef3", common.HexToAddress(hermesID), registrationFee) 298 299 consumerRegistrationFlow(t, c.tequila(), "0xc4cb9a91b8498776f6f8a0d5a2a23beec9b3cef3", "") 300 301 proposal := consumerPicksProposal(t, c.tequila(), "noop") 302 consumerConnectFlow(t, c.tequila(), "0xc4cb9a91b8498776f6f8a0d5a2a23beec9b3cef3", hermesID, "noop", proposal) 303 }) 304 305 t.Run("Consumers rejected by whitelisted service", func(t *testing.T) { 306 c := consumersToTest[0] 307 proposal := consumerPicksProposal(t, c.tequila(), c.serviceType) 308 consumerRejectWhitelistedFlow(t, c.tequila(), c.consumerID, hermesID, c.serviceType, proposal) 309 }) 310 311 t.Run("Registration with bounty", func(t *testing.T) { 312 t.Run("consumer", func(t *testing.T) { 313 c := consumersToTest[0] 314 id, err := c.tequila().NewIdentity("") 315 assert.NoError(t, err) 316 317 err = c.tequila().Unlock(id.Address, "") 318 assert.NoError(t, err) 319 320 status, err := c.tequila().IdentityRegistrationStatus(id.Address) 321 assert.NoError(t, err) 322 assert.Equal(t, "Unregistered", status.Status) 323 324 err = c.tequila().RegisterIdentity(id.Address, "", nil) 325 assert.NoError(t, err) 326 327 assert.Eventually(t, func() bool { 328 status, err := c.tequila().IdentityRegistrationStatus(id.Address) 329 if err != nil { 330 return false 331 } 332 return status.Status == "Registered" 333 }, time.Second*20, time.Millisecond*100) 334 }) 335 t.Run("provider", func(t *testing.T) { 336 c := consumersToTest[0] 337 id, err := c.tequila().NewIdentity("") 338 assert.NoError(t, err) 339 340 err = c.tequila().Unlock(id.Address, "") 341 assert.NoError(t, err) 342 343 status, err := c.tequila().IdentityRegistrationStatus(id.Address) 344 assert.NoError(t, err) 345 assert.Equal(t, "Unregistered", status.Status) 346 347 err = c.tequila().RegisterIdentity(id.Address, "", nil) 348 assert.NoError(t, err) 349 350 assert.Eventually(t, func() bool { 351 status, err := c.tequila().IdentityRegistrationStatus(id.Address) 352 if err != nil { 353 return false 354 } 355 return status.Status == "Registered" 356 }, time.Second*20, time.Millisecond*100) 357 }) 358 }) 359 } 360 361 func recheckBalancesWithHermes(t *testing.T, consumerID string, consumerSpending *big.Int, serviceType, hermesURL string) { 362 var testSuccess bool 363 var lastHermes *big.Int 364 assert.Eventually(t, func() bool { 365 hermesCaller := pingpong.NewHermesCaller(requests.NewHTTPClient("0.0.0.0", time.Second), hermesURL) 366 hermesData, err := hermesCaller.GetConsumerData(chainID, consumerID, -1) 367 assert.NoError(t, err) 368 promised := hermesData.LatestPromise.Amount 369 lastHermes = promised 370 371 // Author: vkuznecovas 372 // Due to the async nature of the payment system, a situation might occur where a consumers reported spending is larger than the actual amount that reaches hermes. 373 // This happens in the following flow: 374 // 1) Session is established. 375 // 2) Payments occur normally for some time. 376 // 3) Consumer received yet another invoice. 377 // 4) Consumer starts calculating the amount to promise. 378 // 5) Session is killed as operation 4) is happening. 379 // 6) The promise is incremented but never issued as the session is aborted. This can happen due to promise not being sent, or provider not listening for further promises on the given topic. 380 // 7) Hermes is not aware of the last promise, therefore the consumer and hermes reportings are different. 381 // I do not believe this will cause issues in reality, as such situations occur in e2e test regularly due to the rapid exchange of promises. 382 // Under normal circumstances, such occurences should be VERY, VERY rare and the amount of myst involved is rather small. They should have no impact on the payment system as a whole. 383 // Therefore, for these tests to be stable, the following solution is proposed: 384 // Make sure that the hermes and consumer reported spendings differ by no more than 1/100000 of a myst. 385 absDiffFloat := getDiffFloat(consumerSpending, promised) 386 res := absDiffFloat < tenthThou 387 if res { 388 testSuccess = true 389 } 390 return res 391 }, time.Second*20, time.Millisecond*300) 392 393 if !testSuccess { 394 fmt.Printf("Consumer reported spending %v hermes says %v. Service type %v. Hermes url %v, consumer ID %v", consumerSpending, lastHermes, serviceType, hermesURL, consumerID) 395 } 396 } 397 398 func getDiffFloat(a, b *big.Int) float64 { 399 diff := new(big.Int).Sub(a, b) 400 absoluteDiff := new(big.Int).Abs(diff) 401 return crypto.BigMystToFloat(absoluteDiff) 402 } 403 404 func identityCreateFlow(t *testing.T, tequilapi *tequilapi_client.Client, idPassphrase string) string { 405 id, err := tequilapi.NewIdentity(idPassphrase) 406 assert.NoError(t, err) 407 log.Info().Msg("Created new identity: " + id.Address) 408 409 return id.Address 410 } 411 412 func initEthClient(t *testing.T) { 413 addr := common.HexToAddress(addressForTopups) 414 ks := keystore.NewKeyStore("/node/keystore", keystore.StandardScryptN, keystore.StandardScryptP) 415 acc, err := ks.Find(accounts.Account{Address: addr}) 416 assert.NoError(t, err) 417 418 err = ks.Unlock(acc, "") 419 assert.NoError(t, err) 420 421 c, err := ethclient.Dial("http://ganache:8545") 422 assert.NoError(t, err) 423 424 ethClient = c 425 426 c2, err := ethclient.Dial("ws://ganache2:8545") 427 assert.NoError(t, err) 428 ethClientL2 = c2 429 430 ethSignerBuilder = func(client *ethclient.Client) func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { 431 chainId, err := client.ChainID(context.Background()) 432 if err != nil { 433 return func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { 434 return nil, err 435 } 436 } 437 return func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { 438 return ks.SignTx(acc, tx, chainId) 439 } 440 } 441 442 } 443 444 func mintMyst(t *testing.T, amount *big.Int, chid common.Address, ethc *ethclient.Client) { 445 ts, err := bindings.NewMystTokenTransactor(common.HexToAddress(mystAddress), ethc) 446 assert.NoError(t, err) 447 448 nonce, err := ethc.PendingNonceAt(context.Background(), common.HexToAddress(addressForTopups)) 449 assert.NoError(t, err) 450 451 _, err = ts.Transfer(&bind.TransactOpts{ 452 From: common.HexToAddress(addressForTopups), 453 Signer: ethSignerBuilder(ethc), 454 Nonce: big.NewInt(0).SetUint64(nonce), 455 }, chid, amount) 456 assert.NoError(t, err) 457 } 458 459 func providerRegistrationFlow(t *testing.T, tequilapi *tequilapi_client.Client, id, idPassphrase string) { 460 err := tequilapi.Unlock(id, idPassphrase) 461 assert.NoError(t, err) 462 463 err = tequilapi.RegisterIdentity(id, "", nil) 464 assert.True(t, err == nil || assert.Contains(t, err.Error(), "registration in progress")) 465 466 assert.Eventually(t, func() bool { 467 idStatus, _ := tequilapi.Identity(id) 468 return idStatus.RegistrationStatus == "Registered" 469 }, time.Second*30, time.Millisecond*500) 470 471 // once we're registered, check some other information 472 idStatus, err := tequilapi.Identity(id) 473 assert.NoError(t, err) 474 475 assert.Equal(t, "Registered", idStatus.RegistrationStatus) 476 assert.Equal(t, providerChannelAddress, idStatus.ChannelAddress) 477 assert.Eventually(t, func() bool { 478 balance, err := tequilapi.BalanceRefresh(id) 479 assert.NoError(t, err) 480 return balanceAfterRegistration.Cmp(balance.Balance) == 0 481 }, time.Second*20, time.Millisecond*500) 482 assert.Zero(t, idStatus.Earnings.Uint64()) 483 assert.Zero(t, idStatus.EarningsTotal.Uint64()) 484 } 485 486 func topUpConsumer(t *testing.T, id string, hermesID common.Address, registrationFee *big.Int) { 487 // TODO: once free registration is a thing of the past, remove this return 488 489 // chid, err := crypto.GenerateChannelAddress(id, hermesID.Hex(), registryAddress, channelImplementation) 490 // assert.NoError(t, err) 491 492 // // add some balance for fees + consuming service 493 // amountToTopUp := big.NewInt(0).Mul(registrationFee, big.NewInt(20)) 494 // mintMyst(t, amountToTopUp, common.HexToAddress(chid)) 495 } 496 497 func consumerRegistrationFlow(t *testing.T, tequilapi *tequilapi_client.Client, id, idPassphrase string) { 498 err := tequilapi.Unlock(id, idPassphrase) 499 assert.NoError(t, err) 500 501 err = tequilapi.RegisterIdentity(id, "", nil) 502 assert.NoError(t, err) 503 504 // now we check identity again 505 err = waitForCondition(func() (bool, error) { 506 regStatus, err := tequilapi.IdentityRegistrationStatus(id) 507 return regStatus.Registered, err 508 }) 509 assert.NoError(t, err) 510 511 idStatus, err := tequilapi.Identity(id) 512 assert.NoError(t, err) 513 assert.Equal(t, "Registered", idStatus.RegistrationStatus) 514 assert.Eventually(t, func() bool { 515 balance, err := tequilapi.BalanceRefresh(id) 516 assert.NoError(t, err) 517 return balanceAfterRegistration.Cmp(balance.Balance) == 0 518 }, time.Second*20, time.Millisecond*500) 519 assert.Zero(t, idStatus.Earnings.Uint64()) 520 assert.Zero(t, idStatus.EarningsTotal.Uint64()) 521 } 522 523 // expect exactly one proposal 524 func consumerPicksProposal(t *testing.T, tequilapi *tequilapi_client.Client, serviceType string) contract.ProposalDTO { 525 var proposals []contract.ProposalDTO 526 assert.Eventually(t, func() bool { 527 p, stateErr := tequilapi.ProposalsByTypeWithWhitelisting(serviceType) 528 if stateErr != nil { 529 log.Err(stateErr) 530 return false 531 } 532 proposals = p 533 return len(p) == 1 534 }, time.Second*30, time.Millisecond*200) 535 536 log.Info().Msgf("Selected proposal is: %v, serviceType=%v", proposals[0], serviceType) 537 return proposals[0] 538 } 539 540 func consumerConnectFlow(t *testing.T, tequilapi *tequilapi_client.Client, consumerID, hermesID, serviceType string, proposal contract.ProposalDTO) *big.Int { 541 connectionStatus, err := tequilapi.ConnectionStatus(0) 542 assert.NoError(t, err) 543 assert.Equal(t, "NotConnected", connectionStatus.Status) 544 545 _, err = tequilapi.ConnectionIP() 546 assert.NoError(t, err) 547 548 err = waitForCondition(func() (bool, error) { 549 status, err := tequilapi.ConnectionStatus(0) 550 return status.Status == "NotConnected", err 551 }) 552 assert.NoError(t, err) 553 554 connectionStatus, err = tequilapi.ConnectionCreate(consumerID, proposal.ProviderID, hermesID, serviceType, contract.ConnectOptions{ 555 DisableKillSwitch: false, 556 }) 557 558 assert.NoError(t, err) 559 560 err = waitForCondition(func() (bool, error) { 561 status, err := tequilapi.ConnectionStatus(0) 562 return status.Status == "Connected", err 563 }) 564 assert.NoError(t, err) 565 566 _, err = tequilapi.ConnectionIP() 567 assert.NoError(t, err) 568 569 // sessions history should be created after connect 570 sessionsDTO, err := tequilapi.SessionsByServiceType(serviceType) 571 assert.NoError(t, err) 572 573 require.True(t, len(sessionsDTO.Items) >= 1) 574 se := sessionsDTO.Items[0] 575 assert.Equal(t, "e2e-land", se.ProviderCountry) 576 assert.Equal(t, serviceType, se.ServiceType) 577 assert.Equal(t, proposal.ProviderID, se.ProviderID) 578 assert.Equal(t, connectionStatus.SessionID, se.ID) 579 assert.Equal(t, "New", se.Status) 580 581 // Wait some time for session to collect stats. 582 assert.Eventually(t, sessionStatsReceived(tequilapi, serviceType), 60*time.Second, 1*time.Second, serviceType) 583 584 err = tequilapi.ConnectionDestroy(0) 585 assert.NoError(t, err) 586 587 err = waitForCondition(func() (bool, error) { 588 status, err := tequilapi.ConnectionStatus(0) 589 return status.Status == "NotConnected", err 590 }) 591 assert.NoError(t, err) 592 593 // sessions history should be updated after disconnect 594 sessionsDTO, err = tequilapi.SessionsByServiceType(serviceType) 595 assert.NoError(t, err) 596 597 assert.True(t, len(sessionsDTO.Items) >= 1) 598 se = sessionsDTO.Items[0] 599 assert.Equal(t, "Completed", se.Status) 600 601 // call the custom asserter for the given service type 602 serviceTypeAssertionMap[serviceType](t, se) 603 consumerStatus := contract.IdentityDTO{} 604 assert.Eventually(t, func() bool { 605 cs, err := tequilapi.Identity(consumerID) 606 if err != nil { 607 return false 608 } 609 consumerStatus = cs 610 return true 611 }, time.Second*20, time.Millisecond*150) 612 assert.True(t, consumerStatus.Balance.Cmp(big.NewInt(0)) == 1, "consumer balance should not be empty") 613 assert.True(t, consumerStatus.Balance.Cmp(balanceAfterRegistration) == -1, "balance should decrease but is %s", consumerStatus.Balance) 614 assert.Zero(t, consumerStatus.Earnings.Uint64()) 615 assert.Zero(t, consumerStatus.EarningsTotal.Uint64()) 616 617 return new(big.Int).Sub(balanceAfterRegistration, consumerStatus.Balance) 618 } 619 620 func consumerRejectWhitelistedFlow(t *testing.T, tequilapi *tequilapi_client.Client, consumerID, accountantID, serviceType string, proposal contract.ProposalDTO) { 621 connectionStatus, err := tequilapi.ConnectionStatus(0) 622 assert.NoError(t, err) 623 assert.Equal(t, "NotConnected", connectionStatus.Status) 624 625 nonVpnIP, err := tequilapi.ConnectionIP() 626 assert.NoError(t, err) 627 log.Info().Msg("Original consumer IP: " + nonVpnIP.IP) 628 629 err = waitForCondition(func() (bool, error) { 630 status, err := tequilapi.ConnectionStatus(0) 631 return status.Status == "NotConnected", err 632 }) 633 assert.NoError(t, err) 634 635 _, err = tequilapi.ConnectionCreate(consumerID, proposal.ProviderID, accountantID, serviceType, contract.ConnectOptions{}) 636 assert.Error(t, err) 637 assert.Contains(t, err.Error(), "consumer identity is not allowed") 638 } 639 640 func providerEarnedTokens(t *testing.T, tequilapi *tequilapi_client.Client, id string, earningsExpected *big.Int) *big.Int { 641 // Before settlement 642 assert.Eventually(t, func() bool { 643 providerStatus, err := tequilapi.Identity(id) 644 if err != nil { 645 return false 646 } 647 648 fdiff := getDiffFloat(providerStatus.Earnings, earningsExpected) 649 return fdiff < tenthThou 650 }, time.Second*20, time.Millisecond*250) 651 652 var providerStatus contract.IdentityDTO 653 var err error 654 assert.Eventually(t, func() bool { 655 providerStatus, err = tequilapi.Identity(id) 656 assert.NoError(t, err) 657 return providerStatus.Balance.Cmp(balanceAfterRegistration) == 0 658 }, time.Second*20, time.Millisecond*500) 659 660 // For reasoning behind these, see the comment in recheckBalancesWithHermes 661 actualEarnings := getDiffFloat(earningsExpected, providerStatus.Earnings) 662 assert.True(t, actualEarnings < tenthThou) 663 664 actualEarnings = getDiffFloat(earningsExpected, providerStatus.EarningsTotal) 665 assert.True(t, actualEarnings < tenthThou) 666 667 assert.True(t, providerStatus.Earnings.Cmp(big.NewInt(500)) == 1, "earnings should be at least 500 but is %d", providerStatus.Earnings) 668 return providerStatus.Earnings 669 } 670 671 func sessionStatsReceived(tequilapi *tequilapi_client.Client, serviceType string) func() bool { 672 var delegate func(stats contract.ConnectionStatisticsDTO) bool 673 if serviceType != "noop" { 674 delegate = func(stats contract.ConnectionStatisticsDTO) bool { 675 return stats.BytesReceived > 0 && stats.BytesSent > 0 && stats.Duration > 45 676 } 677 } else { 678 delegate = func(stats contract.ConnectionStatisticsDTO) bool { 679 return stats.Duration > 45 680 } 681 } 682 683 return func() bool { 684 stats, err := tequilapi.ConnectionStatistics() 685 if err != nil { 686 return false 687 } 688 return delegate(stats) 689 } 690 } 691 692 type sessionAsserter func(t *testing.T, session contract.SessionDTO) 693 694 var serviceTypeAssertionMap = map[string]sessionAsserter{ 695 "openvpn": func(t *testing.T, session contract.SessionDTO) { 696 assert.NotZero(t, session.Duration) 697 assert.NotZero(t, session.BytesSent) 698 assert.NotZero(t, session.BytesReceived) 699 }, 700 "noop": func(t *testing.T, session contract.SessionDTO) { 701 assert.NotZero(t, session.Duration) 702 assert.Zero(t, session.BytesSent) 703 assert.Zero(t, session.BytesReceived) 704 }, 705 "wireguard": func(t *testing.T, session contract.SessionDTO) { 706 assert.NotZero(t, session.Duration) 707 assert.NotZero(t, session.BytesSent) 708 assert.NotZero(t, session.BytesReceived) 709 }, 710 }