Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add OPERATOR tier #3316

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open

feat: Add OPERATOR tier #3316

wants to merge 16 commits into from

Conversation

victor-yanev
Copy link
Contributor

@victor-yanev victor-yanev commented Dec 9, 2024

Description:

Instead of tracking the remaining total budget in-memory inside HbarLimitService (we would have to divide it by the number of Kubernetes instances, etc - it is creating unnecessary complexity in the configurations), we could create a new subscription tier (SubscriptionTier.OPERATOR) and add a pre-configured spending plan in the JSON configuration file which links the EVM address(es) of the relay operator account(s) to it.

In this way we can only set the total limit for the OPERATOR tier to HBAR_RATE_LIMIT_TINYBAR and it will be shared by all instances of the relay.

Related issue(s):

Fixes #3099

Notes for reviewer:

Checklist

  • Documented (Code comments, README, etc.)
  • Tested (unit, integration, etc.)

Signed-off-by: Victor Yanev <[email protected]>
@victor-yanev victor-yanev added the enhancement New feature or request label Dec 9, 2024
@victor-yanev victor-yanev self-assigned this Dec 9, 2024
@victor-yanev victor-yanev added this to the 0.63.0 milestone Dec 9, 2024
@victor-yanev victor-yanev added the Technical Debt Issue which resolves technical debt label Dec 9, 2024
Copy link

github-actions bot commented Dec 9, 2024

Test Results

 20 files   - 1  268 suites   - 47   46m 4s ⏱️ -29s
610 tests +3  603 ✅ + 5  4 💤 ±0  3 ❌  - 2 
686 runs   - 8  679 ✅ ± 0  4 💤 ±0  3 ❌  - 8 

For more details on these failures, see this check.

Results for commit f71eaf6. ± Comparison against base commit c006489.

This pull request removes 1 and adds 4 tests. Note that renamed tests count towards both.
"before all" hook in "@precompile-calls Tests for eth_call with HTS" ‑ RPC Server Acceptance Tests Acceptance tests @precompile-calls Tests for eth_call with HTS "before all" hook in "@precompile-calls Tests for eth_call with HTS"
calls createFungibleToken with custom fees ‑ RPC Server Acceptance Tests Acceptance tests @precompile-calls Tests for eth_call with HTS Create HTS token via direct call to Hedera Token service calls createFungibleToken with custom fees
calls createFungibleToken ‑ RPC Server Acceptance Tests Acceptance tests @precompile-calls Tests for eth_call with HTS Create HTS token via direct call to Hedera Token service calls createFungibleToken
calls createNonFungibleToken with fees ‑ RPC Server Acceptance Tests Acceptance tests @precompile-calls Tests for eth_call with HTS Create HTS token via direct call to Hedera Token service calls createNonFungibleToken with fees
calls createNonFungibleToken ‑ RPC Server Acceptance Tests Acceptance tests @precompile-calls Tests for eth_call with HTS Create HTS token via direct call to Hedera Token service calls createNonFungibleToken

♻️ This comment has been updated with latest results.

@victor-yanev victor-yanev marked this pull request as ready for review December 10, 2024 12:27
konstantinabl
konstantinabl previously approved these changes Dec 10, 2024
Copy link
Collaborator

@konstantinabl konstantinabl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm! but please fix the failing tests in CI

Signed-off-by: Victor Yanev <[email protected]>
Copy link

sonarcloud bot commented Dec 10, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
4.4% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link

codecov bot commented Dec 10, 2024

Codecov Report

Attention: Patch coverage is 87.75510% with 6 lines in your changes missing coverage. Please review.

Project coverage is 85.00%. Comparing base (c006489) to head (f71eaf6).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...s/relay/src/lib/services/hbarLimitService/index.ts 89.47% 3 Missing and 1 partial ⚠️
packages/relay/src/lib/relay.ts 50.00% 0 Missing and 1 partial ⚠️
.../relay/src/lib/services/hapiService/hapiService.ts 87.50% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3316      +/-   ##
==========================================
+ Coverage   84.79%   85.00%   +0.21%     
==========================================
  Files          69       69              
  Lines        4637     4682      +45     
  Branches     1041     1048       +7     
==========================================
+ Hits         3932     3980      +48     
+ Misses        398      392       -6     
- Partials      307      310       +3     
Flag Coverage Δ
config-service 98.14% <ø> (ø)
relay 79.72% <83.67%> (+0.98%) ⬆️
server 83.28% <ø> (ø)
ws-server 36.66% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ckages/config-service/src/services/globalConfig.ts 100.00% <ø> (ø)
...y/src/lib/db/types/hbarLimiter/subscriptionTier.ts 100.00% <100.00%> (ø)
packages/relay/src/lib/relay.ts 91.54% <50.00%> (-1.31%) ⬇️
.../relay/src/lib/services/hapiService/hapiService.ts 79.78% <87.50%> (-0.65%) ⬇️
...s/relay/src/lib/services/hbarLimitService/index.ts 93.12% <89.47%> (+0.42%) ⬆️

... and 6 files with indirect coverage changes

Copy link
Collaborator

@Nana-EC Nana-EC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work.
Some suggestions and questions

duration,
);

const hapiService = new HAPIService(logger, register, this.cacheService, this.eventEmitter, hbarLimitService);

this.clientMain = hapiService.getMainClientInstance();
if (this.clientMain.operatorAccountId) {
hbarLimitService.setOperatorAddress(this.clientMain.operatorAccountId.toSolidityAddress());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: why is this needed when it's set in the hbarLimitService constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's complicated 😄, currently the configuration code in the acceptance tests is quite "spaghetti", I opened a draft PR which attempts to fix it - #3331

@@ -283,43 +283,31 @@ export class GlobalConfig {
envName: 'HBAR_RATE_LIMIT_BASIC',
type: 'number',
required: false,
defaultValue: null,
defaultValue: 1_120_000_000, // 11.2 hbar
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: note the assumed duration for context

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's right below it:
image

@@ -111,7 +111,6 @@ Unless you need to set a non-default value, it is recommended to only populate o
| `REDIS_RECONNECT_DELAY_MS` | "1000" | Sets the delay between reconnect retries from the Redis client in ms |
| `SEND_RAW_TRANSACTION_SIZE_LIMIT` | "131072" | Sets the limit of the transaction size the relay accepts on eth_sendRawTransaction |
| `GET_RECORD_DEFAULT_TO_CONSENSUS_NODE` | "false" | Flag to set if get transaction record logic should first query the mirror node (false) or consensus node via the SDK (true). |
| `HBAR_RATE_LIMIT_WHITELIST` | [""] | An array of EVM addresses that are allowed to bypass the HBAR rate limits |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Was this ever used?
If so what version was this added in?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the old hbar limiter and it's no longer used anywhere, so I removed it

/**
* Relay Operators
*/
OPERATOR = 'OPERATOR',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this requires an update to the design doc.
Did this already happen or will it be done in follow up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I will update the design doc to reflect this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that we do it in a separate PR to not clutter this one even more with changes

Copy link
Member

@quiet-node quiet-node left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work some questions!

const operatorId = ConfigService.get('OPERATOR_ID_MAIN');
const operatorKey = ConfigService.get('OPERATOR_KEY_MAIN');
if (operatorId) {
this.operatorAddress = AccountId.fromString(operatorId as string).toSolidityAddress();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wanted to confirm that toSolidityAddress() will return only the 20-byte address without the prefix 0x, correct? If that's the case should we add the 0x prefix for consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we can do that

* @private
*/
private reset: Date;
private operatorAddress?: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since OPERATOR_ID or OPERATOR_KEY are mandatory config, should we make this as a mandatory variable as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can but we would need to duplicate the really screwed up logic in hapiService.ts:

https://github.com/hashgraph/hedera-json-rpc-relay/blob/main/packages/relay/src/lib/services/hapiService/hapiService.ts#L334-L359

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened another draft PR which fixes the spaghetti code in order to make it really work by only using the ConfigService

Comment on lines +559 to +563
this.logger.trace(`${requestDetails.formattedRequestId} Creating operator spending plan...`);
operatorPlan = await this.createSpendingPlanForAddress(
this.operatorAddress!,
requestDetails,
SubscriptionTier.OPERATOR,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that this aligns with other plans; however, since OPERATOR is a special plan and should have only one account plus should not be updated, was wondering why don't we create operatorPlan directly in the constructor to make it more official?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would still need this logic, so that the operator plan is recreated in case the cache expires (after the limit duration).

Otherwise, if we don't have this on-demand creation, we will need custom logic to create it in intervals of time which could create race conditions and the operator plan might not yet be created for incoming requests.

Comment on lines +105 to +107
} else if (operatorKey) {
this.operatorAddress = Utils.createPrivateKeyBasedOnFormat(operatorKey as string).publicKey.toEvmAddress();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm since the OPERATOR_ID is a mandatory config, I think this will never be reached.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's true but in some cases the OPERATOR_ID is set to an empty string (e.g., in the charts:install workflow)

for (const plan of operatorPlans) {
await hbarSpendingPlanRepository.addToAmountSpent(plan.id, plan.amountSpent, requestDetails, mockTTL);
}

// Note: Since the total HBAR budget is shared across the entire Relay instance by multiple test cases,
// and expense updates occur asynchronously, the wait below ensures that the HBAR amount has sufficient time
// to update properly after each test.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm should we need a new acceptance test to see how new OPERATOR plan work in e2e flow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, the total limit tests already cover this

for (const plan of operatorPlans) {
await hbarSpendingPlanRepository.addToAmountSpent(plan.id, plan.amountSpent, requestDetails, mockTTL);
}

// Note: Since the total HBAR budget is shared across the entire Relay instance by multiple test cases,
// and expense updates occur asynchronously, the wait below ensures that the HBAR amount has sufficient time
// to update properly after each test.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm should we need a new acceptance test to see how new OPERATOR plan work in e2e flow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3316 (comment)

The operator plan essentially replaces the in-memory tracking of the total limit that we used to do before

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Technical Debt Issue which resolves technical debt
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[HBAR Rate Limit Redesign] Add OPERATOR tier
4 participants