Skip to content

Commit

Permalink
test: added e2e test for privileged tier users
Browse files Browse the repository at this point in the history
Signed-off-by: Logan Nguyen <[email protected]>
  • Loading branch information
quiet-node committed Oct 23, 2024
1 parent 4ab3666 commit ab2d303
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 14 deletions.
211 changes: 205 additions & 6 deletions packages/server/tests/acceptance/hbarLimiter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,13 @@ import RelayClient from '../clients/relayClient';
import MirrorClient from '../clients/mirrorClient';
import MetricsClient from '../clients/metricsClient';
import { AliasAccount } from '../types/AliasAccount';
import {
estimateFileTransactionsFee,
overrideEnvsInMochaDescribe
} from '@hashgraph/json-rpc-relay/tests/helpers';
import { ConfigService } from '@hashgraph/json-rpc-config-service/dist/services';
import { ITransfer, RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types';
import { HbarLimitService } from '@hashgraph/json-rpc-relay/dist/lib/services/hbarLimitService';
import { CacheService } from '@hashgraph/json-rpc-relay/dist/lib/services/cacheService/cacheService';
import { SubscriptionTier } from '@hashgraph/json-rpc-relay/dist/lib/db/types/hbarLimiter/subscriptionTier';
import { estimateFileTransactionsFee, overrideEnvsInMochaDescribe } from '@hashgraph/json-rpc-relay/tests/helpers';
import { IDetailedHbarSpendingPlan } from '@hashgraph/json-rpc-relay/dist/lib/db/types/hbarLimiter/hbarSpendingPlan';
import { HbarSpendingPlanRepository } from '@hashgraph/json-rpc-relay/dist/lib/db/repositories/hbarLimiter/hbarSpendingPlanRepository';
import { IPAddressHbarSpendingPlanRepository } from '@hashgraph/json-rpc-relay/dist/lib/db/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository';
import { EthAddressHbarSpendingPlanRepository } from '@hashgraph/json-rpc-relay/dist/lib/db/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository';
Expand Down Expand Up @@ -74,11 +72,14 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () {
metrics: MetricsClient;
relayIsLocal: boolean;
} = global;
const mockTTL = 60000; // 60 secs
const operatorAccount = ConfigService.get('OPERATOR_ID_MAIN') || DOT_ENV.OPERATOR_ID_MAIN || '';
const fileAppendChunkSize = Number(ConfigService.get('FILE_APPEND_CHUNK_SIZE')) || 5120;
const requestId = 'hbarLimiterTest';
const requestDetails = new RequestDetails({ requestId: requestId, ipAddress: '0.0.0.0' });
const cacheService = new CacheService(logger.child({ name: 'cache-service' }), new Registry());
const maxBasicSpendingLimit = HbarLimitService.TIER_LIMITS.BASIC.toTinybars().toNumber();
const maxPrivilegedSpendingLimit = HbarLimitService.TIER_LIMITS.PRIVILEGED.toTinybars().toNumber();

const ethAddressSpendingPlanRepository = new EthAddressHbarSpendingPlanRepository(cacheService, logger);
const ipSpendingPlanRepository = new IPAddressHbarSpendingPlanRepository(cacheService, logger);
Expand Down Expand Up @@ -196,7 +197,6 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () {
before(async function () {
// Restart the relay to reset the limits
await global.restartLocalRelay();
await cacheService.clear(requestDetails);

logger.info(`${requestDetails.formattedRequestId} Creating accounts`);
logger.info(
Expand Down Expand Up @@ -450,7 +450,6 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () {

it('should eventually exhaust the hbar limit for a BASIC user after multiple deployments of large contracts', async function () {
const fileChunkSize = Number(ConfigService.get('FILE_APPEND_CHUNK_SIZE')) || 5120;
const maxBasicSpendingLimit = HbarLimitService.TIER_LIMITS.BASIC.toTinybars().toNumber();
const remainingHbarsBefore = Number(await metrics.get(testConstants.METRICS.REMAINING_HBAR_LIMIT));
const exchangeRateResult = (await mirrorNode.get(`/network/exchangerate`, requestId)).current_rate;
const exchangeRateInCents = exchangeRateResult.cent_equivalent / exchangeRateResult.hbar_equivalent;
Expand Down Expand Up @@ -503,6 +502,206 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () {
}
});
});

describe('NON-BASIC Tiers', () => {
describe('Pre-configured spending plans', async () => {
const expectedPrivilegedEvmAddressAlpha = '0x7d102fe71af42790fe31b126c1f49766376ca2b5';
const expectedPrivilegedEvmAddressBeta = '0x40183ec818c1826114767391989ff2eaebc2b91e';
const expectedPrivilegedIpAddress = '127.0.0.1';
const expectedPrivilegedPlans = {
ALPHA: {
id: 'c758c095-342c-4607-9db5-867d7e90ab9d',
name: 'PRIVILEGED_ALPHA',
ethAddresses: ['0x7d102fe71af42790fe31b126c1f49766376ca2b5'],
ipAddresses: ['127.0.0.1'],
subscriptionTier: 'PRIVILEGED',
},
BETA: {
id: 'a68488b0-6f7d-44a0-87c1-774ad64615f2',
name: 'PRIVILEGED_BETA',
ethAddresses: ['0x40183ec818c1826114767391989ff2eaebc2b91e'],
subscriptionTier: 'PRIVILEGED',
},
};
it('Should successfully populate all pre-configured spending plans', async () => {
Object.values(expectedPrivilegedPlans).forEach(async (plan) => {
const hbarSpendingPlan = await hbarSpendingPlanRepository.findByIdWithDetails(plan.id, requestDetails);
expect(hbarSpendingPlan.id).to.eq(plan.id);
expect(hbarSpendingPlan.active).to.be.true;
expect(hbarSpendingPlan.subscriptionTier).to.eq(SubscriptionTier.PRIVILEGED);
});

const associatedPlanByEVMAddressAlpha = await ethAddressSpendingPlanRepository.findByAddress(
expectedPrivilegedEvmAddressAlpha,
requestDetails,
);
expect(associatedPlanByEVMAddressAlpha.planId).to.eq(expectedPrivilegedPlans.ALPHA.id);
expect(associatedPlanByEVMAddressAlpha.ethAddress).to.eq(expectedPrivilegedEvmAddressAlpha);

const associatedPlanByEVMAddressBeta = await ethAddressSpendingPlanRepository.findByAddress(
expectedPrivilegedEvmAddressBeta,
requestDetails,
);
expect(associatedPlanByEVMAddressBeta.planId).to.eq(expectedPrivilegedPlans.BETA.id);
expect(associatedPlanByEVMAddressBeta.ethAddress).to.eq(expectedPrivilegedEvmAddressBeta);

const associatedPlanByIpAddress = await ipSpendingPlanRepository.findByAddress(
expectedPrivilegedIpAddress,
requestDetails,
);
expect(associatedPlanByIpAddress.planId).to.eq(expectedPrivilegedPlans.ALPHA.id);
expect(associatedPlanByIpAddress.ipAddress).to.eq(expectedPrivilegedIpAddress);
});
});

describe('PRIVILEGED Tier', () => {
let priviledgedEvmAccount: AliasAccount;
let privilegedHbarSpendingPlan: IDetailedHbarSpendingPlan;

beforeEach(async () => {
priviledgedEvmAccount = await Utils.createAliasAccount(
mirrorNode,
global.accounts[0],
requestId,
initialBalance,
);

privilegedHbarSpendingPlan = await hbarSpendingPlanRepository.create(
SubscriptionTier.PRIVILEGED,
requestDetails,
mockTTL,
);

await ethAddressSpendingPlanRepository.save(
{ ethAddress: priviledgedEvmAccount.address, planId: privilegedHbarSpendingPlan.id },
requestDetails,
mockTTL,
);
});

afterEach(async () => {
await hbarSpendingPlanRepository.delete(privilegedHbarSpendingPlan.id, requestDetails);
await ethAddressSpendingPlanRepository.delete(priviledgedEvmAccount.address, requestDetails);
});

it('Should successfully add spending plans', async () => {
const plan = await ethAddressSpendingPlanRepository.findByAddress(
priviledgedEvmAccount.address,
requestDetails,
);

expect(plan.ethAddress).to.eq(priviledgedEvmAccount.address);
expect(plan.planId).to.eq(privilegedHbarSpendingPlan.id);

const spendingPlan = await hbarSpendingPlanRepository.findByIdWithDetails(plan.planId, requestDetails);
expect(spendingPlan.active).to.be.true;
expect(spendingPlan.amountSpent).to.eq(0);
expect(spendingPlan.subscriptionTier).to.eq(SubscriptionTier.PRIVILEGED);
});

it('Should increase the amount spent by the spending plan', async () => {
const contract = await deployContract(largeContractJson, priviledgedEvmAccount.wallet);

// awaiting for HBAR limiter to finish updating expenses in the background
await Utils.wait(6000);

const spendingPlan = await hbarSpendingPlanRepository.findByIdWithDetails(
privilegedHbarSpendingPlan.id,
requestDetails,
);

const expectedCost = await getExpectedCostOfLastLargeTx(contract.deploymentTransaction()!.data);
const amountSpent = spendingPlan.amountSpent;

expect(amountSpent).to.be.approximately(expectedCost, 0.009 * expectedCost);
});

it('Should eventually exhaust the hbar limit for BASIC user but PRIVILEDGED user is still able to make calls', async () => {
let contract: ethers.Contract;
try {
for (let i = 0; i < 50; i++) {
contract = await deployContract(largeContractJson, accounts[2].wallet);
}
expect.fail(`Expected an error but nothing was thrown`);
} catch (e: any) {
expect(e.message).to.contain(predefined.HBAR_RATE_LIMIT_EXCEEDED.message);

// awaiting for HBAR limiter to finish updating expenses in the background
await Utils.wait(6000);

// it should use a different BASIC plan for another user
const gasPrice = await relay.gasPrice(requestId);
const tx = {
...defaultLondonTransactionData,
to: contract!.target,
nonce: await relay.getAccountNonce(priviledgedEvmAccount.address, requestId),
maxPriorityFeePerGas: gasPrice,
maxFeePerGas: gasPrice,
};
const signedTxThird = await priviledgedEvmAccount.wallet.signTransaction(tx);

await expect(
relay.call(testConstants.ETH_ENDPOINTS.ETH_SEND_RAW_TRANSACTION, [signedTxThird], requestId),
).to.be.fulfilled;

const spendingPlan = await hbarSpendingPlanRepository.findByIdWithDetails(
privilegedHbarSpendingPlan.id,
requestDetails,
);
const amountSpent = spendingPlan.amountSpent;
expect(amountSpent).to.lt(maxPrivilegedSpendingLimit);
}
});

it('Should eventually exhaust the hbar limit for PRIVILEDGED user', async () => {
const fileChunkSize = Number(ConfigService.get('FILE_APPEND_CHUNK_SIZE')) || 5120;
const remainingHbarsBefore = Number(await metrics.get(testConstants.METRICS.REMAINING_HBAR_LIMIT));
const exchangeRateResult = (await mirrorNode.get(`/network/exchangerate`, requestId)).current_rate;
const exchangeRateInCents = exchangeRateResult.cent_equivalent / exchangeRateResult.hbar_equivalent;

const factory = new ethers.ContractFactory(
largeContractJson.abi,
largeContractJson.bytecode,
accounts[0].wallet,
);
const deployedTransaction = await factory.getDeployTransaction();
const estimatedTxFee = estimateFileTransactionsFee(
deployedTransaction.data.length,
fileChunkSize,
exchangeRateInCents,
);

try {
for (let i = 0; i < 50; i++) {
await deployContract(largeContractJson, priviledgedEvmAccount.wallet);
}
expect.fail(`Expected an error but nothing was thrown`);
} catch (e: any) {
expect(e.message).to.contain(predefined.HBAR_RATE_LIMIT_EXCEEDED.message);

// awaiting for HBAR limiter to finish updating expenses in the background
await Utils.wait(6000);

const spendingPlanAssociated = await hbarSpendingPlanRepository.findByIdWithDetails(
privilegedHbarSpendingPlan.id,
requestDetails,
);
const amountSpent = spendingPlanAssociated.amountSpent;
const remainingHbarsAfter = Number(await metrics.get(testConstants.METRICS.REMAINING_HBAR_LIMIT));

// Explanation:
// An HBAR limit check triggers the HBAR_RATE_LIMIT_EXCEED error in two scenarios:
// a. if remainingHbarsBefore > maxPrivilegedSpendingLimit ===> (totalHbarSpentByPrivilegedPlan + estimatedTxFee) > maxPrivilegedSpendingLimit
// b. if remainingHbarsBefore <= maxPrivilegedSpendingLimit ===> (remainingBudget - estimatedTxFee) < 0
if (remainingHbarsBefore > maxPrivilegedSpendingLimit) {
expect(amountSpent + estimatedTxFee).to.be.gt(maxPrivilegedSpendingLimit);
} else {
expect(remainingHbarsAfter).to.be.lt(estimatedTxFee);
}
}
});
});
});
});
});
}
Expand Down
6 changes: 4 additions & 2 deletions packages/server/tests/localAcceptance.env
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ LIMIT_DURATION=90000
SERVER_REQUEST_TIMEOUT_MS=60000
MEMWATCH_ENABLED=true
WRITE_SNAPSHOT_ON_MEMORY_LEAK=false
HBAR_RATE_LIMIT_TINYBAR=5000000000# 50 HBARs
HBAR_RATE_LIMIT_DURATION=80000# 80 seconds
HBAR_RATE_LIMIT_TINYBAR=9000000000# 90 HBARs
HBAR_RATE_LIMIT_DURATION=180000# 180 seconds
HBAR_RATE_LIMIT_BASIC=4000000000# 40 HBARs
HBAR_RATE_LIMIT_PRIVILEGED=5000000000# 50 HBARs
HBAR_SPENDING_PLANS_CONFIG_FILE=./packages/server/tests/testSpendingPlansConfig.json
5 changes: 3 additions & 2 deletions packages/server/tests/mainnetAcceptance.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ MIRROR_NODE_URL_WEB3=https://mainnet.mirrornode.hedera.com
E2E_RELAY_HOST=https://mainnet.hashio.io/api
WS_RELAY_URL=wss://mainnet.hashio.io/ws
TEST_INITIAL_ACCOUNT_STARTING_BALANCE=500
HBAR_RATE_LIMIT_TINYBAR=5000000000# 50 HBARs
HBAR_RATE_LIMIT_DURATION=80000# 80 seconds
HBAR_RATE_LIMIT_TINYBAR=9000000000# 90 HBARs
HBAR_RATE_LIMIT_DURATION=180000# 180 seconds
HBAR_RATE_LIMIT_BASIC=4000000000# 40 HBARs
HBAR_RATE_LIMIT_PRIVILEGED=5000000000# 50 HBARs
5 changes: 3 additions & 2 deletions packages/server/tests/previewnetAcceptance.env
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ SERVER_REQUEST_TIMEOUT_MS=60000
MEMWATCH_ENABLED=false
WRITE_SNAPSHOT_ON_MEMORY_LEAK=false
TEST_INITIAL_ACCOUNT_STARTING_BALANCE=500
HBAR_RATE_LIMIT_TINYBAR=5000000000# 50 HBARs
HBAR_RATE_LIMIT_DURATION=80000# 80 seconds
HBAR_RATE_LIMIT_TINYBAR=9000000000# 90 HBARs
HBAR_RATE_LIMIT_DURATION=180000# 180 seconds
HBAR_RATE_LIMIT_BASIC=4000000000# 40 HBARs
HBAR_RATE_LIMIT_PRIVILEGED=5000000000# 50 HBARs
28 changes: 28 additions & 0 deletions packages/server/tests/testSpendingPlansConfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[
{
"id": "c758c095-342c-4607-9db5-867d7e90ab9d",
"name": "PRIVILEGED_ALPHA",
"ethAddresses": ["0x7d102fe71af42790fe31b126c1f49766376ca2b5"],
"ipAddresses": ["127.0.0.1"],
"subscriptionTier": "PRIVILEGED"
},
{
"id": "a68488b0-6f7d-44a0-87c1-774ad64615f2",
"name": "PRIVILEGED_BETA",
"ethAddresses": ["0x40183ec818c1826114767391989ff2eaebc2b91e"],
"subscriptionTier": "PRIVILEGED"
},
{
"id": "af13d6ed-d676-4d33-8b9d-cf05d1ad7134",
"name": "EXTENDED_ALPHA",
"ethAddresses": ["0x149294f355f62827748988071de70ab195d0eb23"],
"ipAddresses": ["127.0.0.2"],
"subscriptionTier": "EXTENDED"
},
{
"id": "7f665aa3-6b73-41d7-bf9b-92d04cdab96b",
"name": "EXTENDED_BETA",
"ipAddresses": ["127.0.0.3"],
"subscriptionTier": "EXTENDED"
}
]
5 changes: 3 additions & 2 deletions packages/server/tests/testnetAcceptance.env
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ SERVER_REQUEST_TIMEOUT_MS=60000
MEMWATCH_ENABLED=false
WRITE_SNAPSHOT_ON_MEMORY_LEAK=false
TEST_INITIAL_ACCOUNT_STARTING_BALANCE=500
HBAR_RATE_LIMIT_TINYBAR=5000000000# 50 HBARs
HBAR_RATE_LIMIT_DURATION=80000# 80 seconds
HBAR_RATE_LIMIT_TINYBAR=9000000000# 90 HBARs
HBAR_RATE_LIMIT_DURATION=180000# 180 seconds
HBAR_RATE_LIMIT_BASIC=4000000000# 40 HBARs
HBAR_RATE_LIMIT_PRIVILEGED=5000000000# 50 HBARs

0 comments on commit ab2d303

Please sign in to comment.