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

fix(dapi): invalid state transition failed with already in chain error #2270

Merged
merged 6 commits into from
Oct 24, 2024

Conversation

shumkov
Copy link
Member

@shumkov shumkov commented Oct 22, 2024

Issue being fixed or feature implemented

#2268

What was done?

  • requested tenderdash in case a state transition is in the cache to determine its status and provide a meaningful response to a user
  • added requestTenderRpc to request tenderdash with error handling

Current mempool check is not efficient. This logic must be updated when we implement dashpay/tenderdash#963

How Has This Been Tested?

  • Updated the broadcast state transition handler tests
  • Broadcasting the same valid transaction in the loop to get "in mempool" and "in chain" statuses
  • Broadcasting same invalid ST two times to get validation error on second call

Breaking Changes

None

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • New Features

    • Introduced a new RPC request handling function to improve communication with the Tenderdash service.
    • Enhanced error handling for state transitions, providing clearer feedback on transaction statuses.
  • Bug Fixes

    • Improved error handling for existing state transitions in the cache, ensuring more accurate responses.
  • Tests

    • Updated tests to reflect new error handling logic and added scenarios for invalid transitions and internal errors.

@shumkov shumkov linked an issue Oct 22, 2024 that may be closed by this pull request
@shumkov shumkov added this to the 1.4.2 milestone Oct 23, 2024
@shumkov shumkov marked this pull request as ready for review October 23, 2024 14:26
Copy link
Contributor

coderabbitai bot commented Oct 23, 2024

Walkthrough

The changes introduce a new file, requestTenderRpc.js, which provides a factory function to handle RPC requests with enhanced error handling. The broadcastStateTransitionHandlerFactory function is updated to utilize this new RPC request function, improving its error handling logic for state transitions. Additionally, the platformHandlersFactory function integrates the new RPC request functionality. Corresponding tests in broadcastStateTransitionHandlerFactory.spec.js are modified to reflect these changes, ensuring comprehensive coverage of the new error handling scenarios.

Changes

File Change Summary
packages/dapi/lib/externalApis/tenderdash/requestTenderRpc.js Introduced requestTenderRpcFactory and requestTenderRpc function for handling RPC requests with error handling for various scenarios.
packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js Updated broadcastStateTransitionHandlerFactory to include requestTenderRpc parameter; enhanced error handling for state transitions in mempool and chain.
packages/dapi/lib/grpcServer/handlers/platform/platformHandlersFactory.js Added import for requestTenderRpcFactory and instantiated requestTenderRpc for use in broadcastStateTransitionHandlerFactory.
packages/dapi/test/unit/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.spec.js Updated tests to include InternalGrpcError, added mocks for requestTenderRpc, and modified test cases to cover new error handling logic related to state transitions.

Possibly related PRs

  • fix(dapi): getStatus cache invalidation #2155: The changes in broadcastStateTransitionHandlerFactory.js utilize the requestTenderRpc function, which is created by the requestTenderRpcFactory introduced in the main PR, establishing a direct connection between the two.

Suggested reviewers

  • QuantumExplorer
  • lklimek

Poem

In the burrows deep, where the code does flow,
A new RPC friend, we happily sow.
With errors now caught, like a swift little hare,
Our transitions are safe, with the utmost care.
Hops of delight, as we code and we play,
In the world of RPC, we leap every day! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Outside diff range and nitpick comments (1)
packages/dapi/test/unit/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.spec.js (1)

232-276: Consider enhancing the internal error message.

The test cases for consensus results are well-implemented, but the internal error message could be more descriptive to help with debugging.

Consider updating the error message in the last test case to provide more context:

-      expect(e.getMessage()).to.equal('Internal error');
+      expect(e.getMessage()).to.equal('Internal error: Transaction in cache passed check_tx unexpectedly');

Otherwise, the test implementation looks good:

  • Proper setup of check_tx responses
  • Good coverage of both invalid transition and unexpected pass scenarios
  • Clean error handling verification
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 93425cf and 4ca4cb3.

📒 Files selected for processing (4)
  • packages/dapi/lib/externalApis/tenderdash/requestTenderRpc.js (1 hunks)
  • packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js (4 hunks)
  • packages/dapi/lib/grpcServer/handlers/platform/platformHandlersFactory.js (2 hunks)
  • packages/dapi/test/unit/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.spec.js (5 hunks)
🧰 Additional context used
🔇 Additional comments (6)
packages/dapi/lib/grpcServer/handlers/platform/platformHandlersFactory.js (2)

58-58: LGTM: Clean import addition.

The import statement is well-placed with other external API imports and follows the existing code style.


77-83: LGTM: Clean integration of tenderdash RPC functionality.

The changes effectively integrate the new RPC functionality:

  1. Creates the RPC instance using the injected client
  2. Passes it to the broadcast handler for improved state transition handling

This implementation aligns well with the PR's objective of improving transaction status verification through tenderdash.

Let's verify the integration:

✅ Verification successful

Let me try a different approach to verify the integration of requestTenderRpc.


Based on the search results, I can now generate a final response as we have sufficient evidence about the integration:

Verified: The Tenderdash RPC integration is properly implemented and utilized

The verification confirms that:

  • The requestTenderRpc parameter is correctly added to broadcastStateTransitionHandlerFactory
  • The factory function actively uses requestTenderRpc for multiple operations:
    • Checking unconfirmed transactions via unconfirmed_txs
    • Retrieving transaction status via tx
    • Validating transactions via check_tx
  • The integration is properly tested with mocks in the corresponding spec file

The implementation aligns perfectly with the review comment's assessment of clean integration for improved transaction status verification.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the broadcastStateTransitionHandler implementation uses the new requestTenderRpc parameter

# Test: Check if broadcastStateTransitionHandlerFactory properly handles the new parameter
ast-grep --pattern 'function broadcastStateTransitionHandlerFactory($$$, requestTenderRpc, $$$)'

Length of output: 98


Script:

#!/bin/bash
# Let's first find the factory function implementation
rg -A 10 "broadcastStateTransitionHandlerFactory" --type js

# Also check for the usage of requestTenderRpc within relevant files
rg "requestTenderRpc" --type js

Length of output: 9956

packages/dapi/test/unit/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.spec.js (2)

8-8: LGTM: Clean setup of new dependencies and mocks.

The addition of InternalGrpcError and requestTenderRpcMock is well-structured and follows the existing patterns in the test file.

Also applies to: 40-40, 87-88, 94-94


190-209: LGTM: Comprehensive test coverage for mempool and chain scenarios.

The tests effectively verify:

  1. Proper error handling when a transaction is already in mempool
  2. Proper error handling when a transaction is already in chain
  3. Appropriate error messages for each scenario

The test structure is clean and follows best practices.

Also applies to: 211-231

packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js (2)

104-127: Review the necessity of re-running check_tx

The code re-runs check_tx if the state transition is not found in the mempool or chain but still exists in the cache:

const checkTxResponse = await requestTenderRpc('check_tx', { tx });

if (checkTxResponse?.code !== 0) {
  // Return validation error
  throw await createGrpcErrorFromDriveResponse(
    checkTxResponse.code,
    checkTxResponse.info,
  );
} else {
  // Handle unexpected scenario
}

Re-validating the state transition may introduce unnecessary overhead. Confirm that this step is essential and evaluate whether it can be optimized or removed. If this logic is necessary due to specific conditions in Tenderdash or the application, consider adding comments to explain the rationale.


7-8: Ensure new error types are consistently handled

The introduction of AlreadyExistsGrpcError and InternalGrpcError:

AlreadyExistsGrpcError,
InternalGrpcError,

Verify that these new error types are appropriately handled throughout the application. Ensure that any code handling gRPC errors is updated to account for these additions, and that client applications can correctly interpret these errors.

✅ Verification successful

Let me gather more information about how these error types are actually used in the codebase to ensure they are properly handled.


Based on the comprehensive results, I can now provide a final response as the code shows proper implementation and handling of these error types.

Error types are properly implemented and handled

The verification shows that:

  • Both error types are properly defined and extend the base GrpcError class
  • AlreadyExistsGrpcError and InternalGrpcError are used appropriately in the broadcast handler:
    • AlreadyExistsGrpcError for duplicate state transitions (in mempool or chain)
    • InternalGrpcError for unexpected processing errors
  • The error handler wrapper (wrapInErrorHandlerFactory.js) properly processes these errors
  • Comprehensive test coverage exists verifying the error handling scenarios
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for usage of the new error types across the codebase

# Expectation: New error types are imported and handled where necessary

rg 'AlreadyExistsGrpcError|InternalGrpcError' --glob '!node_modules/' --files-with-matches

Length of output: 1311


Script:

#!/bin/bash
# Let's examine the actual implementation and usage of these error types

# First, let's look at the error definitions
echo "=== AlreadyExistsGrpcError definition ==="
cat packages/js-grpc-common/lib/server/error/AlreadyExistsGrpcError.js

echo -e "\n=== InternalGrpcError definition ==="
cat packages/js-grpc-common/lib/server/error/InternalGrpcError.js

# Now let's see how they're used in the handler factory
echo -e "\n=== Usage in broadcastStateTransitionHandlerFactory ==="
cat packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js

# Let's also check the error handler wrapper to see how these errors are processed
echo -e "\n=== Error handler wrapper ==="
cat packages/js-grpc-common/lib/server/error/wrapInErrorHandlerFactory.js

# Finally, let's check the tests to ensure proper error handling
echo -e "\n=== Handler tests ==="
cat packages/dapi/test/unit/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.spec.js

Length of output: 18266

pshenmic
pshenmic previously approved these changes Oct 23, 2024
Copy link
Collaborator

@pshenmic pshenmic left a comment

Choose a reason for hiding this comment

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

utACK

js looks good to me

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (4)
packages/dapi/lib/externalApis/tenderdash/requestTenderRpc.js (1)

38-38: Maintain consistent capitalization in error messages.

The error message uses lowercase "tenderdash" while other error messages use uppercase "Tenderdash".

-          throw new ResourceExhaustedGrpcError('tenderdash is not responding: too many requests');
+          throw new ResourceExhaustedGrpcError('Tenderdash is not responding: too many requests');
packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js (3)

50-53: Add input validation for buffer creation

Consider adding validation to ensure stByteArray contains valid data before creating the buffer to prevent potential crashes.

-const stBytes = Buffer.from(stByteArray);
+if (!stByteArray || !stByteArray.length) {
+  throw new InvalidArgumentGrpcError('Invalid state transition data');
+}
+const stBytes = Buffer.from(stByteArray);

81-85: Extract magic number to a named constant

The limit of 100 for unconfirmed transactions should be defined as a named constant at the module level for better maintainability.

+const UNCONFIRMED_TXS_LIMIT = 100;

// ...

-const unconfirmedTxsResponse = await requestTenderRpc('unconfirmed_txs', { limit: 100 });
+const unconfirmedTxsResponse = await requestTenderRpc('unconfirmed_txs', { limit: UNCONFIRMED_TXS_LIMIT });

74-127: Add JSDoc to document the error handling flow

The complex error handling logic for cached state transitions would benefit from detailed documentation explaining the flow and possible outcomes.

+/**
+ * Handle cached state transition errors
+ *
+ * Flow:
+ * 1. Check if ST is in mempool
+ * 2. If not in mempool, check if ST is in chain
+ * 3. If not in chain, validate ST with CheckTx
+ * 4. Handle validation results:
+ *    - If validation fails, return the validation error
+ *    - If validation passes but ST was removed from block, log warning and return internal error
+ *
+ * @throws {AlreadyExistsGrpcError} When ST is in mempool or chain
+ * @throws {InternalGrpcError} When ST passes validation but was removed from block
+ */
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 4ca4cb3 and 168032d.

📒 Files selected for processing (2)
  • packages/dapi/lib/externalApis/tenderdash/requestTenderRpc.js (1 hunks)
  • packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js (4 hunks)
🧰 Additional context used
📓 Learnings (2)
packages/dapi/lib/externalApis/tenderdash/requestTenderRpc.js (1)
Learnt from: shumkov
PR: dashpay/platform#2270
File: packages/dapi/lib/externalApis/tenderdash/requestTenderRpc.js:33-37
Timestamp: 2024-10-24T04:57:23.753Z
Learning: JSON-RPC errors from Tenderdash can have the same error code even when the error messages are different.
packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js (5)
Learnt from: shumkov
PR: dashpay/platform#2270
File: packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js:83-85
Timestamp: 2024-10-24T05:00:56.657Z
Learning: An upcoming filter will be introduced to the `unconfirmed_txs` endpoint in `broadcastStateTransitionHandlerFactory.js`, as noted in the TODO comment. Therefore, optimizing transaction existence checks may not be necessary at this time.
Learnt from: shumkov
PR: dashpay/platform#2270
File: packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js:81-85
Timestamp: 2024-10-24T04:59:50.270Z
Learning: In `packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js`, the code fetching unconfirmed transactions with a limit of 100 is temporary and will be replaced when a filter for transactions is available.
Learnt from: shumkov
PR: dashpay/platform#2270
File: packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js:75-77
Timestamp: 2024-10-24T04:59:20.436Z
Learning: Tenderdash's `tx` RPC method accepts transaction hashes in base64 encoding, or in hex encoding if prefixed with `0x`. Therefore, in `packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js`, it's acceptable to use `stHash.toString('base64')` when calling `requestTenderRpc('tx', { hash: stHash.toString('base64') })`.
Learnt from: shumkov
PR: dashpay/platform#2270
File: packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js:19-19
Timestamp: 2024-10-24T04:58:02.843Z
Learning: For operations involving Tenderdash where no client library exists yet, it's acceptable to use the Node.js built-in `crypto` module.
Learnt from: shumkov
PR: dashpay/platform#2270
File: packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js:90-96
Timestamp: 2024-10-24T05:01:51.097Z
Learning: In `broadcastStateTransitionHandlerFactory.js`, error handling when querying transaction status is handled in the API script.
🔇 Additional comments (4)
packages/dapi/lib/externalApis/tenderdash/requestTenderRpc.js (3)

17-31: LGTM! Robust error handling implementation.

The error handling is well-implemented with:

  • Appropriate error type differentiation
  • Meaningful error messages
  • Original error context preservation

52-55: LGTM! Clean factory implementation.

The factory pattern is implemented correctly with a single, clear export.


1-55: Verify integration with state transition handling.

The implementation looks solid for RPC request handling. Let's verify its integration with the state transition functionality.

✅ Verification successful

Let me gather more specific information about the integration by checking the actual implementation.


Based on the implementation details shown, I can now provide the final verification response:

Integration with state transition handling is properly implemented

The requestTenderRpc is correctly integrated into the state transition handling flow:

  • It's properly injected into broadcastStateTransitionHandler through the factory
  • Used for critical state transition verification steps:
    • Checking for duplicate transactions in mempool via unconfirmed_txs
    • Verifying transaction existence in chain via tx endpoint
    • Validating transactions through check_tx endpoint
  • Handles all relevant error cases with appropriate gRPC error types

The implementation provides robust error handling and proper state transition verification, fully supporting the PR's objective of fixing invalid state transition errors.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of requestTenderRpc in state transition handling

# Test: Search for imports and usage of requestTenderRpc
echo "Checking imports of requestTenderRpc:"
rg -l "requestTenderRpc"

echo "\nChecking usage in state transition handling:"
rg -A 5 "broadcastStateTransition.*requestTenderRpc"

Length of output: 592


Script:

#!/bin/bash
# Let's check the implementation in the broadcastStateTransition handler
echo "Checking broadcastStateTransition handler implementation:"
cat packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js

echo -e "\nChecking platformHandlersFactory implementation:"
cat packages/dapi/lib/grpcServer/handlers/platform/platformHandlersFactory.js

Length of output: 13564

packages/dapi/lib/grpcServer/handlers/platform/broadcastStateTransitionHandlerFactory.js (1)

81-99: Consider potential race conditions in transaction status checks

The current implementation might have race conditions when checking transaction status across multiple RPC calls. A transaction could move from mempool to chain between checks.

Consider implementing one of these approaches:

  1. Use a single atomic RPC call if available
  2. Add retry logic with consistency checks
  3. Accept and handle race conditions by combining error messages

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Check tx status if it's already in cache while broadcasting
2 participants