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

[SLT-234] feat(rfq): dynamic block conf wait + receipt x event quote check w/ RPC consensus #3190

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

golangisfun123
Copy link
Collaborator

@golangisfun123 golangisfun123 commented Sep 25, 2024

Description
A clear and concise description of the features you're adding in this pull request.

Additional context
Add any other context about the problem you're solving.

Metadata

  • Fixes #[Link to Issue]

Summary by CodeRabbit

  • New Features

    • Introduced a VolumeLimit configuration for specific backend chains to enhance transaction management.
    • Added methods to retrieve chain-specific confirmation limits and volume limits.
  • Bug Fixes

    • Improved transaction validation by adding a receipt verification step.
  • Tests

    • Updated test cases to align with the new transaction structures and parameters, enhancing coverage and accuracy.
  • Chores

    • Modified configuration settings for blockchain chains to streamline transaction processing.

Copy link
Contributor

coderabbitai bot commented Sep 25, 2024

Walkthrough

The pull request introduces significant modifications across various files related to the relayer configuration and transaction handling. Key changes include the addition of a VolumeLimit field in specific configurations, enhancements to the rate limiter's logic for transaction validation, and updates to test cases to align with new transaction structures. Additionally, new methods are added to retrieve chain-specific confirmation limits, and the overall configuration structure is reorganized to improve clarity and functionality.

Changes

File Path Change Summary
services/rfq/e2e/setup_test.go Added VolumeLimit field with value 10_000 in two configuration instances; removed global VolumeLimit declaration.
services/rfq/relayer/limiter/limiter.go Introduced checkReceipt method; updated IsAllowed logic to include volume limit checks; renamed and modified withinSizeLimit to isUnderVolumeLimit.
services/rfq/relayer/limiter/limiter_test.go Updated test cases to reflect new transaction structures; added helper functions for transaction data simulation.
services/rfq/relayer/limiter/suite_test.go Updated chain configuration for chain ID 10; added new configuration for chain ID 81457 with specific parameters.
services/rfq/relayer/relconfig/config.go Removed VolumeLimit from Config; added VolumeLimit and RPCConfirmations to ChainConfig.
services/rfq/relayer/relconfig/getters.go Added GetRPCConfirmations method; modified GetVolumeLimit to use chain-specific limits.
services/rfq/util/util.go Updated QuoteRequestToAttributes function to accept a pointer to QuoteRequest instead of a value.

Possibly related PRs

  • RFQ API: add GET /ack endpoint #2643: The main PR modifies the getRelayerConfig function to include a VolumeLimit field, which is also relevant in the context of the changes made in this PR that involve configuration settings for the RFQ API.
  • RFQ API: fix prometheus setup #2701: This PR includes changes to the handling of transaction statuses, which may relate to the modifications in the main PR that involve the management of configuration settings and limits.
  • RFQ Relayer: restrict state transitions #2787: The changes in this PR regarding state transitions and status checks are relevant to the main PR's focus on managing configurations and limits in the relayer.
  • RFQ Relayer: add scroll rebalancing #2863: The introduction of new configuration parameters for managing confirmations and limits in this PR aligns with the changes made in the main PR regarding the VolumeLimit.
  • RFQ Relayer: configure chain confirmations #2967: The modifications to the NewChain function to accept configuration settings are directly related to the changes in the main PR that involve the VolumeLimit and other configuration parameters.
  • fix(rfq-relayer): e2e test flakes #3075: The improvements made to the e2e tests in this PR may indirectly relate to the changes in the main PR by ensuring that the configurations are tested thoroughly.

Suggested labels

M-docs

Suggested reviewers

  • dwasse
  • trajan0x
  • aureliusbtc

Poem

In the meadow where bunnies play,
New limits and checks have come our way!
With volume set and confirmations right,
Our transactions will hop with delight!
So let's cheer for the code that's bright,
A relayer's dream, taking flight! 🐇✨


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 353b63c and 5bc3004.

📒 Files selected for processing (1)
  • services/rfq/relayer/limiter/limiter.go (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/rfq/relayer/limiter/limiter.go

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.

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.

@github-actions github-actions bot added go Pull requests that update Go code size/m labels Sep 25, 2024
@golangisfun123 golangisfun123 changed the title [SLT-234] feat(rfq): multiple rpc receipt after RFQ confirmation delay [SLT-234] feat(rfq): multiple rpc receipt check after RFQ confirmation delay Sep 25, 2024
Copy link

codecov bot commented Sep 25, 2024

Codecov Report

Attention: Patch coverage is 55.66038% with 47 lines in your changes missing coverage. Please review.

Project coverage is 33.07430%. Comparing base (f0b13bc) to head (5bc3004).
Report is 135 commits behind head on master.

Files with missing lines Patch % Lines
services/rfq/relayer/limiter/limiter.go 66.29213% 24 Missing and 6 partials ⚠️
services/rfq/relayer/service/statushandler.go 0.00000% 9 Missing ⚠️
services/rfq/relayer/relconfig/getters.go 0.00000% 7 Missing ⚠️
services/rfq/util/util.go 0.00000% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (f0b13bc) and HEAD (5bc3004). Click for more details.

HEAD has 2 uploads less than BASE
Flag BASE (f0b13bc) HEAD (5bc3004)
solidity 2 0
Additional details and impacted files
@@                 Coverage Diff                 @@
##              master       #3190         +/-   ##
===================================================
- Coverage   40.97804%   33.07430%   -7.90374%     
===================================================
  Files            459         535         +76     
  Lines          25643       33715       +8072     
  Branches         343          82        -261     
===================================================
+ Hits           10508       11151        +643     
- Misses         14383       21575       +7192     
- Partials         752         989        +237     
Flag Coverage Δ
opbot 0.48870% <ø> (ø)
promexporter 6.81642% <ø> (?)
solidity ?

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

cloudflare-workers-and-pages bot commented Sep 25, 2024

Deploying sanguine-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: bfd1761
Status: ✅  Deploy successful!
Preview URL: https://eafc0c77.sanguine-fe.pages.dev
Branch Preview URL: https://rpc-confirmations.sanguine-fe.pages.dev

View logs

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: 8

🧹 Outside diff range and nitpick comments (6)
services/rfq/relayer/service/statushandler.go (1)

90-97: LGTM! Consider enhancing error handling.

The addition of the confirmationsClient is a good improvement, aligning with the PR's objective of handling multiple RPC receipts after RFQ confirmation delay. This change provides more precise control over transaction confirmations.

A minor suggestion for improvement:

Consider wrapping the error returned from GetConfirmationsClient with additional context. This can be done using the %w verb in fmt.Errorf. For example:

-		return nil, fmt.Errorf("could not get confirmations client: %w", err)
+		return nil, fmt.Errorf("could not get confirmations client for chain %d: %w", req.Transaction.OriginChainId, err)

This change would provide more specific information about which chain caused the error, making debugging easier.

Also applies to: 118-118

services/rfq/relayer/relconfig/config.go (1)

111-115: LGTM! Consider clarifying the VolumeLimit comment.

The addition of VolumeLimit and RPCConfirmations fields to the ChainConfig struct enhances the system's flexibility and security. The RPCConfirmations field, in particular, adds a valuable safeguard against potential reorg issues.

Consider updating the comment for VolumeLimit to clarify the time frame for the volume limit. For example:

- // VolumeLimit is the maximum dollar value of relayed transactions in the BlockWindow.
+ // VolumeLimit is the maximum dollar value of relayed transactions allowed within a specified time frame or block window.

This change would provide more context about when and how the volume limit is applied.

services/rfq/relayer/relconfig/getters.go (1)

852-860: LGTM! Consider improving error handling.

The new GetLimitConfirmations method is a good addition for retrieving chain-specific limit confirmations. However, returning 0 for non-existent chain configurations might lead to unexpected behavior if not handled properly by the caller.

Consider returning an error along with the value to explicitly indicate when a chain configuration is not found:

-func (c Config) GetLimitConfirmations(chainID int) uint64 {
+func (c Config) GetLimitConfirmations(chainID int) (uint64, error) {
 	chainConfig, ok := c.Chains[chainID]
 	if !ok {
-		return 0
+		return 0, fmt.Errorf("chain configuration not found for chainID: %d", chainID)
 	}
-	return chainConfig.LimitConfirmations
+	return chainConfig.LimitConfirmations, nil
}
services/rfq/relayer/limiter/limiter.go (1)

100-100: Update attribute key to reflect volume limit check

The attribute key "within_size_limit" may be misleading since the function now checks volume limits. It would be clearer to rename it to "under_volume_limit" to accurately represent what is being measured.

Apply this diff to rename the attribute:

100 - attribute.Bool("within_size_limit", underVolumeLimit),
100 + attribute.Bool("under_volume_limit", underVolumeLimit),
services/rfq/relayer/limiter/limiter_test.go (2)

257-269: Replace hardcoded hex strings with programmatically generated log data.

The hardcoded hex string in lines 262-269 reduces readability and can introduce errors. Additionally, there's a TODO comment at line 261 indicating the intent to fix the log data creation. Consider programmatically generating prefixData to enhance clarity and maintainability.

Would you like assistance in generating the log data programmatically? I can help refactor this section or open a GitHub issue to track this task.


218-219: Ensure error handling for TransactionReceipt in mock client.

In lines 218-219, the TransactionReceipt method is mocked without simulating possible errors. Consider adding error scenarios to thoroughly test how your limiter handles failures in retrieving transaction receipts.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3af3438 and 0aa10b6.

📒 Files selected for processing (7)
  • services/rfq/e2e/setup_test.go (2 hunks)
  • services/rfq/relayer/limiter/limiter.go (7 hunks)
  • services/rfq/relayer/limiter/limiter_test.go (2 hunks)
  • services/rfq/relayer/limiter/suite_test.go (2 hunks)
  • services/rfq/relayer/relconfig/config.go (1 hunks)
  • services/rfq/relayer/relconfig/getters.go (1 hunks)
  • services/rfq/relayer/service/statushandler.go (2 hunks)
🔇 Additional comments not posted (10)
services/rfq/relayer/limiter/suite_test.go (5)

48-51: LGTM: New parameters added to chain configuration.

The addition of RPCConfirmations, LimitConfirmations, VolumeLimit, and RFQAddress parameters to the chain configuration is consistent and looks good.


50-50: Verify the difference in VolumeLimit between chains.

The VolumeLimit for chain 10 is set to 1000 USD, while for chain 81457 it's set to 10000 USD. Please confirm if this difference is intentional and aligns with the expected behavior for these chains.

Also applies to: 68-68


51-51: Check RFQAddress consistency across chains.

The RFQAddress is set to the same value for both chains. Please verify if this is the intended configuration or if each chain should have a unique RFQAddress.

Also applies to: 69-69


Line range hint 36-64: Verify token configurations across chains.

The token configurations (USDC and ETH) are identical for both chains. Please confirm if this is correct or if there should be chain-specific token configurations.


35-35: Verify the impact of changing chain ID from 1 to 10.

The chain ID has been updated from 1 to 10. Please ensure that this change is consistent with other parts of the codebase and doesn't break any existing functionality.

✅ Verification successful

Chain ID change from 1 to 10 is verified and does not break existing functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for occurrences of chain ID 1 in the codebase
rg --type go "chainId.*1" --glob '!*test*'

Length of output: 44


Script:

#!/bin/bash
# Search for various representations of chain ID in Go files excluding tests
rg --type go "(chainID\s*[:=]\s*1)|(chain_id\s*[:=]\s*1)|(CHAIN_ID\s*[:=]\s*1)" --glob '!*test*'

Length of output: 98

services/rfq/relayer/relconfig/getters.go (2)

843-851: LGTM! Improved flexibility with chain-specific volume limits.

The modification to use chainCfg.VolumeLimit instead of c.VolumeLimit enhances the system's flexibility by allowing different volume limits for different chains. This change aligns well with the evolving requirements of the system.


861-870: LGTM! Well-implemented with a sensible default.

The new GetRPCConfirmations method is a good addition for retrieving chain-specific RPC confirmations. The use of a default value (1) for non-existent chain configurations is a sensible approach, ensuring a minimum of one confirmation.

services/rfq/relayer/limiter/limiter.go (2)

215-219: Clarify error handling for transaction receipt retrieval

When checking for ethereum.NotFound error, the logic returns false, nil, indicating that the receipt does not exist, which is acceptable. However, for any other errors, it returns false with an error. Ensure this behavior is intended and consider logging unexpected errors for better observability.

Review if additional error logging is necessary. If so, you might want to add logging for unexpected errors:

218 + l.metrics.Logger().Errorf("Unexpected error when getting transaction receipt: %v", err)

253-260: Review the completeness of the rfqFieldsMatch function

The rfqFieldsMatch function compares several fields to determine if the event matches the request. The TODO comment suggests uncertainty about its exhaustiveness. Ensure that all relevant fields are compared to prevent false positives.

Would you like assistance in reviewing and updating the rfqFieldsMatch function to ensure it comprehensively compares all necessary fields?

services/rfq/relayer/limiter/limiter_test.go (1)

204-204: Verify the correctness of GetPrice mock return value.

In line 204, the GetPrice method of mockQuoter is mocked to return a fixed price. Ensure that this price accurately reflects the expected scenarios in your tests.

Run the following script to check all usages of GetPrice and confirm the returned values align with test expectations:

services/rfq/e2e/setup_test.go Show resolved Hide resolved
span.SetAttributes(
attribute.String("receipt txHash", receipt.TxHash.Hex()),
attribute.String("receipt log address", receipt.ContractAddress.String()),
attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent potential index out-of-range panic when accessing receipt logs

Accessing receipt.Logs[0] without checking if receipt.Logs is non-empty can result in a runtime panic if the logs slice is empty. To ensure robustness, add a check to verify that receipt.Logs contains at least one entry before accessing it.

Apply this diff to add the necessary check:

245 + if len(receipt.Logs) > 0 {
245     attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
245 + } else {
245 +     attribute.String("receipt data", "no logs available"),
245 + }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
if len(receipt.Logs) > 0 {
attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
} else {
attribute.String("receipt data", "no logs available"),
}

if err != nil {
return false, fmt.Errorf("could not get required confirmations from config: %w", err)
}
requiredConfirmations := l.cfg.GetLimitConfirmations(int(request.Transaction.OriginChainId))
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle potential errors when retrieving required confirmations

In the hasEnoughConfirmations function, the retrieval of requiredConfirmations does not handle potential errors from l.cfg.GetLimitConfirmations(). If this function can return an error, it should be properly handled to prevent unexpected behavior.

Modify the code to handle the error:

117 - requiredConfirmations := l.cfg.GetLimitConfirmations(int(request.Transaction.OriginChainId))
+ requiredConfirmations, err := l.cfg.GetLimitConfirmations(int(request.Transaction.OriginChainId))
+ if err != nil {
+     return false, fmt.Errorf("could not get required confirmations: %w", err)
+ }

Committable suggestion was skipped due to low confidence.

Comment on lines +20 to +21
originAmount, _ = new(big.Float).SetFloat64(11983199386503458).Int(nil)
destAmount, _ = new(big.Float).SetFloat64(11980157553261996).Int(nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle potential inaccuracies when converting big.Float to *big.Int.

In lines 20-21, the second return value from (*big.Float).Int(nil) is being ignored using _. This second value indicates the accuracy of the conversion. Ignoring it might lead to unintended results if the conversion is inexact. Consider handling the accuracy explicitly to ensure the integrity of originAmount and destAmount.

// Basically, I used some random RFQ transaction as the test case for this suite,
// but mocked the block and price that's returned instead of changing the actual transaction fields.
// This is why the data is hardcoded.
// TODO: fix this once I figure out how to properly create the log data
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address the TODO comment regarding log data creation.

The TODO at line 261 suggests that the way log data is currently created is temporary. Finalizing this implementation will improve test reliability and code quality.

mockListener := buildMockListener(6)
packedBridgeTx, err := packBridgeTransaction(quote.Transaction)
l.NoError(err)
mockClient := buildMockEVMClient(quoteRequestToReceipt(quote, packedBridgeTx, false))
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check for potential nil dereference in mockClient.

When calling buildMockEVMClient, ensure that the returned mockClient is not nil before using it to prevent potential runtime panics.

Comment on lines +223 to +255
bridgeStruct, err := abi.NewType(
"tuple",
"BridgeTransaction",
[]abi.ArgumentMarshaling{
{Name: "originChainId", Type: "uint32"},
{Name: "destChainId", Type: "uint32"},
{Name: "originSender", Type: "address"},
{Name: "destRecipient", Type: "address"},
{Name: "originToken", Type: "address"},
{Name: "destToken", Type: "address"},
{Name: "originAmount", Type: "uint256"},
{Name: "destAmount", Type: "uint256"},
{Name: "originFeeAmount", Type: "uint256"},
{Name: "sendChainGas", Type: "bool"},
{Name: "deadline", Type: "uint256"},
{Name: "nonce", Type: "uint256"},
})
if err != nil {
//nolint: wrapcheck
return []byte{}, err
}

args := abi.Arguments{
{Type: bridgeStruct, Name: "param_one"},
}

packed, err := args.Pack(&tx)
if err != nil {
//nolint: wrapcheck
return []byte{}, err
}
return packed, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor packBridgeTransaction to improve maintainability.

The packBridgeTransaction function manually constructs the ABI type and packs the transaction data. This approach can be error-prone and hard to maintain. Consider using the ABI generated by abigen or directly referencing the contract's ABI to simplify the process and reduce potential errors.

For example, if you have an ABI binding for your contract, you can modify the function as follows:

-func packBridgeTransaction(tx fastbridge.IFastBridgeBridgeTransaction) ([]byte, error) {
-	// manual ABI packing code
-	...
-	return packed, nil
+func packBridgeTransaction(tx fastbridge.IFastBridgeBridgeTransaction) ([]byte, error) {
+	return fastbridge.ABI.Pack("YourFunctionName", tx)
+}

Replace "YourFunctionName" with the appropriate function name from your contract's ABI.

Committable suggestion was skipped due to low confidence.

@golangisfun123 golangisfun123 changed the title [SLT-234] feat(rfq): multiple rpc receipt check after RFQ confirmation delay [SLT-234] feat(rfq): dynamic block conf wait + receipt x event quote check w/ RPC consensus Sep 30, 2024
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: 4

🧹 Outside diff range and nitpick comments (7)
services/rfq/relayer/relconfig/getters.go (2)

843-851: Approve change, but test coverage needed.

The modification to use chainCfg.VolumeLimit instead of c.VolumeLimit is a logical improvement, allowing for chain-specific volume limits. However, this change is not covered by tests, which is a concern.

Would you like assistance in writing test cases to cover this change in the GetVolumeLimit function?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 843-843: services/rfq/relayer/relconfig/getters.go#L843
Added line #L843 was not covered by tests


852-861: Approve new function, but improvements needed.

The new GetRPCConfirmations function is a good addition, providing a way to retrieve chain-specific RPC confirmation settings. However, there are a few points to address:

  1. Consider adding a comment to explain the purpose and significance of RPC confirmations.
  2. This new function is not covered by tests, which should be addressed to ensure its correctness and maintain code quality.

Would you like assistance in writing test cases for the new GetRPCConfirmations function and adding appropriate documentation?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 855-860: services/rfq/relayer/relconfig/getters.go#L855-L860
Added lines #L855 - L860 were not covered by tests

services/rfq/relayer/limiter/limiter.go (5)

Line range hint 113-115: Remove unnecessary error check after calling LatestBlock

The function l.listener.LatestBlock() returns a uint64 without an error, but there is an error check immediately after that assumes an error might have occurred. This check is invalid and may lead to confusion.

Apply this diff to remove the unnecessary error check:

 currentBlockNumber := l.listener.LatestBlock()
-if err != nil {
-    return false, fmt.Errorf("could not get block number: %w", err)
-}

181-181: Use consistent attribute naming for clarity

In the span attributes, the key "within_size_limit" is used. For consistency and clarity, consider renaming it to "under_volume_limit" to match the variable underVolumeLimit and make the logs more intuitive.

Apply this diff to rename the attribute:

 attribute.String("token_price", tokenPrice.String()),
-attribute.Bool("within_size_limit", underVolumeLimit),
+attribute.Bool("under_volume_limit", underVolumeLimit),

240-246: Handle potential nil receipt to prevent panic

While the TransactionReceipt method should return a receipt when err is nil, it's good practice to check if receipt is not nil before using it. This ensures robustness in case of unexpected behavior from the RPC client.

Apply this diff to add a nil check:

 receipt, err := l.evmClient.TransactionReceipt(ctx, request.OriginTxHash)
 if err != nil {
     if errors.Is(err, ethereum.NotFound) {
         return false, nil
     }
     return false, fmt.Errorf("could not get transaction receipt: %w", err)
+} else if receipt == nil {
+    return false, fmt.Errorf("received nil receipt without error")
 }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 242-245: services/rfq/relayer/limiter/limiter.go#L242-L245
Added lines #L242 - L245 were not covered by tests


76-94: Add unit tests to cover new error handling branches

Several new error handling branches have been introduced in the IsAllowed method, specifically when checking volume limits, confirmations, and receipts. Currently, these branches are not covered by tests, as indicated by the static analysis tool. To improve test coverage and ensure robustness, please add unit tests that simulate these error conditions.

Would you like assistance in creating these unit tests or opening a GitHub issue to track this task?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 76-76: services/rfq/relayer/limiter/limiter.go#L76
Added line #L76 was not covered by tests


[warning] 85-86: services/rfq/relayer/limiter/limiter.go#L85-L86
Added lines #L85 - L86 were not covered by tests


[warning] 93-94: services/rfq/relayer/limiter/limiter.go#L93-L94
Added lines #L93 - L94 were not covered by tests


146-149: Add context to the error message for better debugging

When returning an error from getRequestVolumeOfToken, including the token information can aid in debugging. This provides more insight into which token caused the error.

Apply this diff to enhance the error message:

 if err != nil {
-    return 0, fmt.Errorf("could not get token volume: %w", err)
+    return 0, fmt.Errorf("could not get volume for token %s: %w", request.Transaction.OriginToken.String(), err)
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0aa10b6 and 7fca370.

📒 Files selected for processing (6)
  • services/rfq/relayer/limiter/limiter.go (6 hunks)
  • services/rfq/relayer/limiter/limiter_test.go (2 hunks)
  • services/rfq/relayer/limiter/suite_test.go (2 hunks)
  • services/rfq/relayer/relconfig/config.go (1 hunks)
  • services/rfq/relayer/relconfig/getters.go (1 hunks)
  • services/rfq/util/util.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • services/rfq/relayer/limiter/limiter_test.go
  • services/rfq/relayer/limiter/suite_test.go
  • services/rfq/relayer/relconfig/config.go
🧰 Additional context used
🪛 GitHub Check: codecov/patch
services/rfq/relayer/limiter/limiter.go

[warning] 76-76: services/rfq/relayer/limiter/limiter.go#L76
Added line #L76 was not covered by tests


[warning] 85-86: services/rfq/relayer/limiter/limiter.go#L85-L86
Added lines #L85 - L86 were not covered by tests


[warning] 93-94: services/rfq/relayer/limiter/limiter.go#L93-L94
Added lines #L93 - L94 were not covered by tests


[warning] 242-245: services/rfq/relayer/limiter/limiter.go#L242-L245
Added lines #L242 - L245 were not covered by tests


[warning] 250-251: services/rfq/relayer/limiter/limiter.go#L250-L251
Added lines #L250 - L251 were not covered by tests


[warning] 255-256: services/rfq/relayer/limiter/limiter.go#L255-L256
Added lines #L255 - L256 were not covered by tests


[warning] 261-261: services/rfq/relayer/limiter/limiter.go#L261
Added line #L261 was not covered by tests


[warning] 269-275: services/rfq/relayer/limiter/limiter.go#L269-L275
Added lines #L269 - L275 were not covered by tests

services/rfq/relayer/relconfig/getters.go

[warning] 843-843: services/rfq/relayer/relconfig/getters.go#L843
Added line #L843 was not covered by tests


[warning] 855-860: services/rfq/relayer/relconfig/getters.go#L855-L860
Added lines #L855 - L860 were not covered by tests

🔇 Additional comments (3)
services/rfq/util/util.go (1)

15-15: Approved: Performance improvement by using pointer receiver

The change from func QuoteRequestToAttributes(request reldb.QuoteRequest) to func QuoteRequestToAttributes(request *reldb.QuoteRequest) is a good optimization. It avoids unnecessary copying of the QuoteRequest struct, which can improve performance, especially if QuoteRequest is a large struct.

services/rfq/relayer/relconfig/getters.go (1)

843-861: Summary: Improvements to chain-specific configurations, but test coverage needed.

The changes in this file introduce more granular control over chain-specific settings, which is a positive improvement. However, it's crucial to address the lack of test coverage for both the modified GetVolumeLimit function and the new GetRPCConfirmations function.

To maintain code quality and ensure the correctness of these changes, please add appropriate test cases covering:

  1. The use of chainCfg.VolumeLimit in the GetVolumeLimit function.
  2. The entire GetRPCConfirmations function, including edge cases.

To help verify the impact of these changes, you can run the following command to check for existing tests related to these functions:

This will help identify if there are any existing tests that need to be updated or if entirely new tests need to be written.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 843-843: services/rfq/relayer/relconfig/getters.go#L843
Added line #L843 was not covered by tests


[warning] 855-860: services/rfq/relayer/relconfig/getters.go#L855-L860
Added lines #L855 - L860 were not covered by tests

services/rfq/relayer/limiter/limiter.go (1)

226-276: Ensure the receipt parsing handles all expected events

In the checkReceipt function, the code currently checks for the event *fastbridge.FastBridgeBridgeRequested. If there are other relevant events that should be considered, make sure to include them or confirm that this is exhaustive.

Run the following script to list all events emitted by the contract and verify if additional events need to be handled:

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 242-245: services/rfq/relayer/limiter/limiter.go#L242-L245
Added lines #L242 - L245 were not covered by tests


[warning] 250-251: services/rfq/relayer/limiter/limiter.go#L250-L251
Added lines #L250 - L251 were not covered by tests


[warning] 255-256: services/rfq/relayer/limiter/limiter.go#L255-L256
Added lines #L255 - L256 were not covered by tests


[warning] 261-261: services/rfq/relayer/limiter/limiter.go#L261
Added line #L261 was not covered by tests


[warning] 269-275: services/rfq/relayer/limiter/limiter.go#L269-L275
Added lines #L269 - L275 were not covered by tests

@@ -12,7 +12,7 @@ import (
var EthAddress = common.HexToAddress("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE")

// QuoteRequestToAttributes converts a quote request to attributes.
func QuoteRequestToAttributes(request reldb.QuoteRequest) []attribute.KeyValue {
func QuoteRequestToAttributes(request *reldb.QuoteRequest) []attribute.KeyValue {
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 30, 2024

Choose a reason for hiding this comment

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

💡 Codebase verification

Issues Found: Calls to QuoteRequestToAttributes Without Pointers

  • services/rfq/relayer/limiter/limiter.go:67: trace.WithAttributes(util.QuoteRequestToAttributes(request)...) is passing request by value instead of a pointer.
🔗 Analysis chain

Verify all calls to QuoteRequestToAttributes in the codebase

This change in function signature requires updating all calls to QuoteRequestToAttributes throughout the codebase. Ensure that all calling code now passes a pointer to QuoteRequest instead of a value.

Run the following script to find all occurrences of QuoteRequestToAttributes and verify they're passing a pointer:

Please review the output and ensure all calls are updated correctly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all calls to QuoteRequestToAttributes and verify they're passing a pointer

# Search for QuoteRequestToAttributes calls
echo "Searching for QuoteRequestToAttributes calls:"
rg --type go -n "QuoteRequestToAttributes\s*\(" -A 3

echo "\nVerifying pointer usage:"
# Look for &reldb.QuoteRequest or &variableName patterns in the same line or next line
rg --type go -n "QuoteRequestToAttributes\s*\((\s*&|\n\s*&)"

echo "\nPotential issues (calls without &):"
# Look for calls without & (potential issues)
rg --type go -n "QuoteRequestToAttributes\s*\([^&\n]"

Length of output: 1457

Copy link
Collaborator Author

@golangisfun123 golangisfun123 Sep 30, 2024

Choose a reason for hiding this comment

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

wrong limiter.go:67 is fine

Copy link
Contributor

Choose a reason for hiding this comment

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

@golangisfun123, thank you for the clarification. You're right—limiter.go:67 is correct as is, and passing request without using & is appropriate since request is already a pointer. I'll treat this as a warning rather than an issue in the future.

(•́﹏•̀)

🔗 Analysis chain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that all calls to QuoteRequestToAttributes are passing pointers

echo "Searching for calls to QuoteRequestToAttributes where the argument is not a pointer:"

rg --type go -n 'QuoteRequestToAttributes\(\s*[^&]' 

Length of output: 479


✏️ Learnings added
Learnt from: golangisfun123
PR: synapsecns/sanguine#3190
File: services/rfq/util/util.go:15-15
Timestamp: 2024-09-30T19:02:29.722Z
Learning: In `limiter.go`, the `request` variable passed to `QuoteRequestToAttributes` is already a pointer, so no modification is needed.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


// isUnderVolumeLimit returns true if the request is under the volume limit, false otherwise.
func (l *limiterImpl) isUnderVolumeLimit(ctx context.Context, request *reldb.QuoteRequest) (_ bool, err error) {
ctx, span := l.metrics.Tracer().Start(ctx, "limiter.underVolumeLimitLimit")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the tracer name to reflect the function accurately

The tracer name in isUnderVolumeLimit is currently set to "limiter.underVolumeLimitLimit", which seems redundant and may lead to confusion. It should be corrected to "limiter.isUnderVolumeLimit" to match the function name and maintain consistency.

Apply this diff to update the tracer name:

 ctx, span := l.metrics.Tracer().Start(ctx, 
-    "limiter.underVolumeLimitLimit")
+    "limiter.isUnderVolumeLimit")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ctx, span := l.metrics.Tracer().Start(ctx, "limiter.underVolumeLimitLimit")
ctx, span := l.metrics.Tracer().Start(ctx, "limiter.isUnderVolumeLimit")

Comment on lines +272 to +273
attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check for empty receipt logs before accessing the first element

Accessing receipt.Logs[0] without verifying that receipt.Logs is not empty can lead to an index out-of-range panic if the slice is empty. Before accessing receipt.Logs[0].Data, ensure that there is at least one log entry in receipt.Logs.

Apply this diff to add the necessary check:

 span.SetAttributes(
     attribute.String("receipt txHash", receipt.TxHash.Hex()),
     attribute.String("receipt log address", receipt.ContractAddress.String()),
-    attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
+    attribute.String("receipt data", func() string {
+        if len(receipt.Logs) > 0 {
+            return hex.EncodeToString(receipt.Logs[0].Data)
+        }
+        return "no logs available"
+    }()),
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
)
span.SetAttributes(
attribute.String("receipt txHash", receipt.TxHash.Hex()),
attribute.String("receipt log address", receipt.ContractAddress.String()),
attribute.String("receipt data", func() string {
if len(receipt.Logs) > 0 {
return hex.EncodeToString(receipt.Logs[0].Data)
}
return "no logs available"
}()),
)

Comment on lines 151 to 156
volumeLimitForChain := l.cfg.GetVolumeLimit(int(request.Transaction.OriginChainId), request.Transaction.OriginToken)

return uint64(new(big.Int).Div(
tokenVolume,
volumeLimitForChain,
).Int64()), nil
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent potential division by zero

In the function getNumberOfConfirmationsToWait, there is a division operation where tokenVolume is divided by volumeLimitForChain. If volumeLimitForChain is zero, this will cause a runtime panic due to division by zero. Ensure that volumeLimitForChain is not zero before performing the division.

Apply this diff to add a check for zero:

 volumeLimitForChain := l.cfg.GetVolumeLimit(int(request.Transaction.OriginChainId), request.Transaction.OriginToken)

+if volumeLimitForChain.Cmp(big.NewInt(0)) == 0 {
+    return 0, fmt.Errorf("volume limit for chain is zero, cannot divide by zero")
+}

 return uint64(new(big.Int).Div(
     tokenVolume,
     volumeLimitForChain,
 ).Int64()), nil
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
volumeLimitForChain := l.cfg.GetVolumeLimit(int(request.Transaction.OriginChainId), request.Transaction.OriginToken)
return uint64(new(big.Int).Div(
tokenVolume,
volumeLimitForChain,
).Int64()), nil
volumeLimitForChain := l.cfg.GetVolumeLimit(int(request.Transaction.OriginChainId), request.Transaction.OriginToken)
if volumeLimitForChain.Cmp(big.NewInt(0)) == 0 {
return 0, fmt.Errorf("volume limit for chain is zero, cannot divide by zero")
}
return uint64(new(big.Int).Div(
tokenVolume,
volumeLimitForChain,
).Int64()), nil

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: 4

🧹 Outside diff range and nitpick comments (2)
services/rfq/relayer/limiter/limiter.go (2)

Line range hint 162-187: Renamed and updated isUnderVolumeLimit method is an improvement

The renaming of withinSizeLimit to isUnderVolumeLimit enhances clarity and better reflects the method's purpose. The updated logic using the new getRequestVolumeOfToken method is appropriate and consistent with the overall changes.

However, there's a minor inconsistency:

Update the attribute name for consistency:

 span.SetAttributes(
     attribute.String("volume_limit", volumeLimit.String()),
     attribute.String("token_price", tokenPrice.String()),
-    attribute.Bool("within_size_limit", underVolumeLimit),
+    attribute.Bool("under_volume_limit", underVolumeLimit),
 )

This change aligns the attribute name with the new method name and improves consistency in logging.


Improve test coverage for critical methods

The static analysis indicates that several important methods in the limiter.go file are not covered by existing tests. Specifically, the following methods lack adequate unit tests:

  • IsAllowed
  • getNumberOfConfirmationsToWait
  • checkReceipt

Ensuring comprehensive test coverage for these methods is essential to maintain the reliability and correctness of the rate limiter functionality.

🔗 Analysis chain

Line range hint 1-290: Improve test coverage for new and modified code

The static analysis tool has flagged several new and modified code sections as not covered by tests. This includes critical parts of the IsAllowed, getNumberOfConfirmationsToWait, and checkReceipt methods.

Please add or update unit tests to cover the following areas:

  1. Error handling in IsAllowed method (lines 76, 85-86, 93-94)
  2. Error handling in getNumberOfConfirmationsToWait method (lines 148-149)
  3. Various branches in checkReceipt method (lines 245-248, 253-254, 258-259, 264, 272-278)

Ensuring good test coverage will help maintain the reliability and correctness of the rate limiter as the codebase evolves.

This will help identify existing test files where new tests can be added.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# List test files related to the limiter package
fd -e go -p 'limiter.*test' --exec echo "Test file: {}"

Length of output: 352


Script:

#!/bin/bash
# Corrected command to list test files related to the limiter package
fd -e go -p 'limiter.*test\.go$' --exec echo "Test file: {}"

Length of output: 241


Script:

#!/bin/bash
# Search for test functions related to specific methods in the limiter package tests
rg -i 'func Test.*(IsAllowed|getNumberOfConfirmationsToWait|checkReceipt)' ./services/rfq/relayer/limiter/*_test.go

Length of output: 168

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 76-76: services/rfq/relayer/limiter/limiter.go#L76
Added line #L76 was not covered by tests


[warning] 85-86: services/rfq/relayer/limiter/limiter.go#L85-L86
Added lines #L85 - L86 were not covered by tests


[warning] 93-94: services/rfq/relayer/limiter/limiter.go#L93-L94
Added lines #L93 - L94 were not covered by tests

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 7fca370 and 353b63c.

📒 Files selected for processing (1)
  • services/rfq/relayer/limiter/limiter.go (6 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
services/rfq/relayer/limiter/limiter.go

[warning] 76-76: services/rfq/relayer/limiter/limiter.go#L76
Added line #L76 was not covered by tests


[warning] 85-86: services/rfq/relayer/limiter/limiter.go#L85-L86
Added lines #L85 - L86 were not covered by tests


[warning] 93-94: services/rfq/relayer/limiter/limiter.go#L93-L94
Added lines #L93 - L94 were not covered by tests


[warning] 120-120: services/rfq/relayer/limiter/limiter.go#L120
Added line #L120 was not covered by tests


[warning] 148-149: services/rfq/relayer/limiter/limiter.go#L148-L149
Added lines #L148 - L149 were not covered by tests


[warning] 245-248: services/rfq/relayer/limiter/limiter.go#L245-L248
Added lines #L245 - L248 were not covered by tests


[warning] 253-254: services/rfq/relayer/limiter/limiter.go#L253-L254
Added lines #L253 - L254 were not covered by tests


[warning] 258-259: services/rfq/relayer/limiter/limiter.go#L258-L259
Added lines #L258 - L259 were not covered by tests


[warning] 264-264: services/rfq/relayer/limiter/limiter.go#L264
Added line #L264 was not covered by tests


[warning] 272-278: services/rfq/relayer/limiter/limiter.go#L272-L278
Added lines #L272 - L278 were not covered by tests

🔇 Additional comments (3)
services/rfq/relayer/limiter/limiter.go (3)

6-7: New imports and struct field addition look good

The new imports and the addition of the evmClient field to the limiterImpl struct are well-aligned with the new receipt verification functionality. These changes provide the necessary tools to interact with the Ethereum blockchain and verify transaction receipts.

Also applies to: 11-11, 14-15, 42-42


52-52: NewRateLimiter function update is appropriate

The addition of the evmClient parameter to the NewRateLimiter function and its assignment to the struct field is consistent with the new functionality. This change ensures that the rate limiter has access to the Ethereum client for receipt verification.

Also applies to: 60-60


Line range hint 1-290: Summary of limiter.go changes

The changes to limiter.go significantly enhance the rate limiting logic and introduce important new features like receipt verification. Key improvements include:

  1. More comprehensive checks in the IsAllowed method.
  2. Dynamic confirmation waiting based on transaction volume.
  3. Receipt verification to handle potential reorg scenarios.

While these changes are generally positive, please address the following points:

  1. Improve error handling and error messages for better debugging.
  2. Add safeguards against potential panics (division by zero, nil pointer dereference).
  3. Ensure consistency in naming (e.g., attribute names in spans).
  4. Review and possibly expand the RFQ field matching in rfqFieldsMatch.
  5. Significantly improve test coverage for new and modified code.

Addressing these points will further improve the robustness and maintainability of the rate limiter implementation.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 76-76: services/rfq/relayer/limiter/limiter.go#L76
Added line #L76 was not covered by tests


[warning] 85-86: services/rfq/relayer/limiter/limiter.go#L85-L86
Added lines #L85 - L86 were not covered by tests


[warning] 93-94: services/rfq/relayer/limiter/limiter.go#L93-L94
Added lines #L93 - L94 were not covered by tests

Comment on lines +73 to +95
// if not enough confirmations, check volume. if under limit, wait 0 confirmations.
underVolumeLimit, err := l.isUnderVolumeLimit(ctx, request)
if err != nil {
return false, fmt.Errorf("could not check confirmations: %w", err)
return false, fmt.Errorf("could not check volume limit: %w", err)
}
if hasEnoughConfirmations {
if underVolumeLimit {
return true, nil
}

// if not enough confirmations, check volume
withinSize, err := l.withinSizeLimit(ctx, request)
// if enough confirmations, allow because reorgs are rare at this point.
hasEnoughConfirmations, err := l.hasEnoughConfirmations(ctx, request)
if err != nil {
return false, fmt.Errorf("could not check volume limit: %w", err)
return false, fmt.Errorf("could not check confirmations: %w", err)
}
if hasEnoughConfirmations {
// we need to check if the receipt exists, parse the events from it, check for possible reverts,
// and has the correct fields in case of a reorg. then, and only then, we can be sure a reorg will not
// revert this.
receiptFieldsMatch, err := l.checkReceipt(ctx, request)
if err != nil {
return false, fmt.Errorf("could not check receipt: %w", err)
}
return receiptFieldsMatch, nil
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improved IsAllowed method logic

The new logic in the IsAllowed method provides a more comprehensive check for request validation. The sequence of checks (volume limit, confirmations, and receipt verification) enhances the security and reliability of the rate limiting process.

However, consider improving error handling for better debugging:

Consider wrapping errors with more context:

- return false, fmt.Errorf("could not check volume limit: %w", err)
+ return false, fmt.Errorf("IsAllowed failed to check volume limit: %w", err)

- return false, fmt.Errorf("could not check confirmations: %w", err)
+ return false, fmt.Errorf("IsAllowed failed to check confirmations: %w", err)

- return false, fmt.Errorf("could not check receipt: %w", err)
+ return false, fmt.Errorf("IsAllowed failed to check receipt: %w", err)

This change will make it easier to trace the origin of errors in logs.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// if not enough confirmations, check volume. if under limit, wait 0 confirmations.
underVolumeLimit, err := l.isUnderVolumeLimit(ctx, request)
if err != nil {
return false, fmt.Errorf("could not check confirmations: %w", err)
return false, fmt.Errorf("could not check volume limit: %w", err)
}
if hasEnoughConfirmations {
if underVolumeLimit {
return true, nil
}
// if not enough confirmations, check volume
withinSize, err := l.withinSizeLimit(ctx, request)
// if enough confirmations, allow because reorgs are rare at this point.
hasEnoughConfirmations, err := l.hasEnoughConfirmations(ctx, request)
if err != nil {
return false, fmt.Errorf("could not check volume limit: %w", err)
return false, fmt.Errorf("could not check confirmations: %w", err)
}
if hasEnoughConfirmations {
// we need to check if the receipt exists, parse the events from it, check for possible reverts,
// and has the correct fields in case of a reorg. then, and only then, we can be sure a reorg will not
// revert this.
receiptFieldsMatch, err := l.checkReceipt(ctx, request)
if err != nil {
return false, fmt.Errorf("could not check receipt: %w", err)
}
return receiptFieldsMatch, nil
// if not enough confirmations, check volume. if under limit, wait 0 confirmations.
underVolumeLimit, err := l.isUnderVolumeLimit(ctx, request)
if err != nil {
return false, fmt.Errorf("IsAllowed failed to check volume limit: %w", err)
}
if underVolumeLimit {
return true, nil
}
// if enough confirmations, allow because reorgs are rare at this point.
hasEnoughConfirmations, err := l.hasEnoughConfirmations(ctx, request)
if err != nil {
return false, fmt.Errorf("IsAllowed failed to check confirmations: %w", err)
}
if hasEnoughConfirmations {
// we need to check if the receipt exists, parse the events from it, check for possible reverts,
// and has the correct fields in case of a reorg. then, and only then, we can be sure a reorg will not
// revert this.
receiptFieldsMatch, err := l.checkReceipt(ctx, request)
if err != nil {
return false, fmt.Errorf("IsAllowed failed to check receipt: %w", err)
}
return receiptFieldsMatch, nil
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 76-76: services/rfq/relayer/limiter/limiter.go#L76
Added line #L76 was not covered by tests


[warning] 85-86: services/rfq/relayer/limiter/limiter.go#L85-L86
Added lines #L85 - L86 were not covered by tests


[warning] 93-94: services/rfq/relayer/limiter/limiter.go#L93-L94
Added lines #L93 - L94 were not covered by tests

Comment on lines 137 to 160
// getNumberOfConfirmationsToWait returns the number of confirmations to wait for the request.
// confirmations = requestVolume / volumeLimitForChain, e.g. we wait 1 confirmation per every `volumeLimitForChain` USD.
func (l *limiterImpl) getNumberOfConfirmationsToWait(ctx context.Context, request *reldb.QuoteRequest) (_ uint64, err error) {
ctx, span := l.metrics.Tracer().Start(ctx, "limiter.getNumberOfConfirmationsToWait")

defer func() {
metrics.EndSpanWithErr(span, err)
}()

tokenVolume, err := l.getRequestVolumeOfToken(ctx, request)
if err != nil {
return 0, fmt.Errorf("could not get token volume: %w", err)
}

volumeLimitForChain := l.cfg.GetVolumeLimit(int(request.Transaction.OriginChainId), request.Transaction.OriginToken)
if volumeLimitForChain.Cmp(big.NewInt(-1)) == 0 {
return 0, nil
}

return uint64(new(big.Int).Div(
tokenVolume,
volumeLimitForChain,
).Int64()), nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

New getNumberOfConfirmationsToWait method looks good, but needs a safeguard

The new getNumberOfConfirmationsToWait method provides a dynamic way to determine the required confirmations based on the request volume and chain-specific volume limit. The logic is sound, including the special case handling for no limit (when volumeLimitForChain is -1).

However, there's a potential issue:

Add a check to prevent division by zero:

 volumeLimitForChain := l.cfg.GetVolumeLimit(int(request.Transaction.OriginChainId), request.Transaction.OriginToken)
 if volumeLimitForChain.Cmp(big.NewInt(-1)) == 0 {
     return 0, nil
 }
+if volumeLimitForChain.Cmp(big.NewInt(0)) == 0 {
+    return 0, fmt.Errorf("volume limit for chain %d and token %s is zero", request.Transaction.OriginChainId, request.Transaction.OriginToken)
+}

 return uint64(new(big.Int).Div(
     tokenVolume,
     volumeLimitForChain,
 ).Int64()), nil

This change will prevent a potential panic if the volume limit is set to zero for a chain/token combination.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// getNumberOfConfirmationsToWait returns the number of confirmations to wait for the request.
// confirmations = requestVolume / volumeLimitForChain, e.g. we wait 1 confirmation per every `volumeLimitForChain` USD.
func (l *limiterImpl) getNumberOfConfirmationsToWait(ctx context.Context, request *reldb.QuoteRequest) (_ uint64, err error) {
ctx, span := l.metrics.Tracer().Start(ctx, "limiter.getNumberOfConfirmationsToWait")
defer func() {
metrics.EndSpanWithErr(span, err)
}()
tokenVolume, err := l.getRequestVolumeOfToken(ctx, request)
if err != nil {
return 0, fmt.Errorf("could not get token volume: %w", err)
}
volumeLimitForChain := l.cfg.GetVolumeLimit(int(request.Transaction.OriginChainId), request.Transaction.OriginToken)
if volumeLimitForChain.Cmp(big.NewInt(-1)) == 0 {
return 0, nil
}
return uint64(new(big.Int).Div(
tokenVolume,
volumeLimitForChain,
).Int64()), nil
}
// getNumberOfConfirmationsToWait returns the number of confirmations to wait for the request.
// confirmations = requestVolume / volumeLimitForChain, e.g. we wait 1 confirmation per every `volumeLimitForChain` USD.
func (l *limiterImpl) getNumberOfConfirmationsToWait(ctx context.Context, request *reldb.QuoteRequest) (_ uint64, err error) {
ctx, span := l.metrics.Tracer().Start(ctx, "limiter.getNumberOfConfirmationsToWait")
defer func() {
metrics.EndSpanWithErr(span, err)
}()
tokenVolume, err := l.getRequestVolumeOfToken(ctx, request)
if err != nil {
return 0, fmt.Errorf("could not get token volume: %w", err)
}
volumeLimitForChain := l.cfg.GetVolumeLimit(int(request.Transaction.OriginChainId), request.Transaction.OriginToken)
if volumeLimitForChain.Cmp(big.NewInt(-1)) == 0 {
return 0, nil
}
if volumeLimitForChain.Cmp(big.NewInt(0)) == 0 {
return 0, fmt.Errorf("volume limit for chain %d and token %s is zero", request.Transaction.OriginChainId, request.Transaction.OriginToken)
}
return uint64(new(big.Int).Div(
tokenVolume,
volumeLimitForChain,
).Int64()), nil
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 148-149: services/rfq/relayer/limiter/limiter.go#L148-L149
Added lines #L148 - L149 were not covered by tests

Comment on lines +230 to +279
// checkReceipt checks if the receipt exists and has the correct fields in the case a reorg happened.
func (l *limiterImpl) checkReceipt(ctx context.Context, request *reldb.QuoteRequest) (_ bool, err error) {
// Make sure receipt exists and has the correct fields in case of a reorg.
// Note: https://community.infura.io/t/does-eth-gettransactionreceipt-respond-to-re-orged-transactions/7765
// "You will get a tx receipt back but, as you note, there is a small chance of a reorg.
// In a re-org the shorter side chain (one block usually, two occasionally) will have all its tx’s reverted and
// placed back in the mempool. Calling eth_getTransactionReceipt at this point will return null, until the tx is
// added to a new block and validated."
ctx, span := l.metrics.Tracer().Start(ctx, "limiter.checkReceipt")
defer func() {
metrics.EndSpanWithErr(span, err)
}()

receipt, err := l.evmClient.TransactionReceipt(ctx, request.OriginTxHash)
if err != nil {
if errors.Is(err, ethereum.NotFound) {
return false, nil
}
return false, fmt.Errorf("could not get transaction receipt: %w", err)
}

rfqAddr, err := l.cfg.GetRFQAddress(int(request.Transaction.OriginChainId))
if err != nil {
return false, fmt.Errorf("could not get RFQ address: %w", err)
}

parser, err := fastbridge.NewParser(rfqAddr)
if err != nil {
return false, fmt.Errorf("could not create parser: %w", err)
}

for _, log := range receipt.Logs {
_, parsedEvent, ok := parser.ParseEvent(*log)
if !ok {
continue
}

event, ok := parsedEvent.(*fastbridge.FastBridgeBridgeRequested)
if ok {
return rfqFieldsMatch(request, event) && !log.Removed, nil
}
}
span.SetAttributes(
attribute.String("receipt txHash", receipt.TxHash.Hex()),
attribute.String("receipt log address", receipt.ContractAddress.String()),
attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
)

return false, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

New checkReceipt method enhances security, but needs a safeguard

The checkReceipt method is a valuable addition that verifies transaction receipts and handles potential reorg scenarios. The event parsing and field matching logic provide a robust check for transaction validity.

However, there's a potential issue:

Add a check before accessing log data to prevent a potential nil pointer dereference:

 span.SetAttributes(
     attribute.String("receipt txHash", receipt.TxHash.Hex()),
     attribute.String("receipt log address", receipt.ContractAddress.String()),
-    attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
+    attribute.String("receipt data", func() string {
+        if len(receipt.Logs) > 0 {
+            return hex.EncodeToString(receipt.Logs[0].Data)
+        }
+        return "no logs available"
+    }()),
 )

This change will prevent a panic if the receipt has no logs, which could happen in certain edge cases.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// checkReceipt checks if the receipt exists and has the correct fields in the case a reorg happened.
func (l *limiterImpl) checkReceipt(ctx context.Context, request *reldb.QuoteRequest) (_ bool, err error) {
// Make sure receipt exists and has the correct fields in case of a reorg.
// Note: https://community.infura.io/t/does-eth-gettransactionreceipt-respond-to-re-orged-transactions/7765
// "You will get a tx receipt back but, as you note, there is a small chance of a reorg.
// In a re-org the shorter side chain (one block usually, two occasionally) will have all its tx’s reverted and
// placed back in the mempool. Calling eth_getTransactionReceipt at this point will return null, until the tx is
// added to a new block and validated."
ctx, span := l.metrics.Tracer().Start(ctx, "limiter.checkReceipt")
defer func() {
metrics.EndSpanWithErr(span, err)
}()
receipt, err := l.evmClient.TransactionReceipt(ctx, request.OriginTxHash)
if err != nil {
if errors.Is(err, ethereum.NotFound) {
return false, nil
}
return false, fmt.Errorf("could not get transaction receipt: %w", err)
}
rfqAddr, err := l.cfg.GetRFQAddress(int(request.Transaction.OriginChainId))
if err != nil {
return false, fmt.Errorf("could not get RFQ address: %w", err)
}
parser, err := fastbridge.NewParser(rfqAddr)
if err != nil {
return false, fmt.Errorf("could not create parser: %w", err)
}
for _, log := range receipt.Logs {
_, parsedEvent, ok := parser.ParseEvent(*log)
if !ok {
continue
}
event, ok := parsedEvent.(*fastbridge.FastBridgeBridgeRequested)
if ok {
return rfqFieldsMatch(request, event) && !log.Removed, nil
}
}
span.SetAttributes(
attribute.String("receipt txHash", receipt.TxHash.Hex()),
attribute.String("receipt log address", receipt.ContractAddress.String()),
attribute.String("receipt data", hex.EncodeToString(receipt.Logs[0].Data)),
)
return false, nil
}
// checkReceipt checks if the receipt exists and has the correct fields in the case a reorg happened.
func (l *limiterImpl) checkReceipt(ctx context.Context, request *reldb.QuoteRequest) (_ bool, err error) {
// Make sure receipt exists and has the correct fields in case of a reorg.
// Note: https://community.infura.io/t/does-eth-gettransactionreceipt-respond-to-re-orged-transactions/7765
// "You will get a tx receipt back but, as you note, there is a small chance of a reorg.
// In a re-org the shorter side chain (one block usually, two occasionally) will have all its tx's reverted and
// placed back in the mempool. Calling eth_getTransactionReceipt at this point will return null, until the tx is
// added to a new block and validated."
ctx, span := l.metrics.Tracer().Start(ctx, "limiter.checkReceipt")
defer func() {
metrics.EndSpanWithErr(span, err)
}()
receipt, err := l.evmClient.TransactionReceipt(ctx, request.OriginTxHash)
if err != nil {
if errors.Is(err, ethereum.NotFound) {
return false, nil
}
return false, fmt.Errorf("could not get transaction receipt: %w", err)
}
rfqAddr, err := l.cfg.GetRFQAddress(int(request.Transaction.OriginChainId))
if err != nil {
return false, fmt.Errorf("could not get RFQ address: %w", err)
}
parser, err := fastbridge.NewParser(rfqAddr)
if err != nil {
return false, fmt.Errorf("could not create parser: %w", err)
}
for _, log := range receipt.Logs {
_, parsedEvent, ok := parser.ParseEvent(*log)
if !ok {
continue
}
event, ok := parsedEvent.(*fastbridge.FastBridgeBridgeRequested)
if ok {
return rfqFieldsMatch(request, event) && !log.Removed, nil
}
}
span.SetAttributes(
attribute.String("receipt txHash", receipt.TxHash.Hex()),
attribute.String("receipt log address", receipt.ContractAddress.String()),
attribute.String("receipt data", func() string {
if len(receipt.Logs) > 0 {
return hex.EncodeToString(receipt.Logs[0].Data)
}
return "no logs available"
}()),
)
return false, nil
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 245-248: services/rfq/relayer/limiter/limiter.go#L245-L248
Added lines #L245 - L248 were not covered by tests


[warning] 253-254: services/rfq/relayer/limiter/limiter.go#L253-L254
Added lines #L253 - L254 were not covered by tests


[warning] 258-259: services/rfq/relayer/limiter/limiter.go#L258-L259
Added lines #L258 - L259 were not covered by tests


[warning] 264-264: services/rfq/relayer/limiter/limiter.go#L264
Added line #L264 was not covered by tests


[warning] 272-278: services/rfq/relayer/limiter/limiter.go#L272-L278
Added lines #L272 - L278 were not covered by tests

Comment on lines +281 to +290
// TODO: is this exhaustive?
func rfqFieldsMatch(request *reldb.QuoteRequest, event *fastbridge.FastBridgeBridgeRequested) bool {
transactionIDMatch := request.TransactionID == event.TransactionId
senderMatch := request.Sender.String() == event.Sender.String()
originAmountMatch := request.Transaction.OriginAmount.String() == event.OriginAmount.String()
destAmountMatch := request.Transaction.DestAmount.String() == event.DestAmount.String()
originTokenMatch := request.Transaction.OriginToken.String() == event.OriginToken.String()

return transactionIDMatch && senderMatch && originAmountMatch && destAmountMatch && originTokenMatch
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

rfqFieldsMatch function is not exhaustive

The rfqFieldsMatch function currently verifies transaction ID, sender, origin amount, destination amount, and origin token. However, the BridgeRequested event includes additional fields that should also be matched to ensure comprehensive validation:

  1. Destination Chain ID (destChainId)
  2. Destination Token (destToken)
  3. Send Chain Gas (sendChainGas)

Please update the rfqFieldsMatch function to include these fields to address the TODO comment and enhance the reliability of the verification process.

🔗 Analysis chain

New rfqFieldsMatch function is comprehensive, but needs review

The rfqFieldsMatch function provides a thorough comparison of RFQ fields with event data, which is crucial for verifying the integrity of the transaction. The checks cover all essential fields: transaction ID, sender, amounts, and token.

However, there's an open question:

The TODO comment suggests that the field matching might not be exhaustive. Please review if there are any additional fields that should be checked. Consider adding checks for:

  1. Destination chain ID
  2. Destination token
  3. Recipient address

If these fields are not available in the event data, update the comment to explain why the current checks are sufficient.

This will help ensure that all necessary fields are being verified and improve the robustness of the check.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the BridgeRequested event definition to verify available fields
ast-grep --lang solidity --pattern 'event BridgeRequested(
  $$$
)'

Length of output: 352


Script:

#!/bin/bash
# Search for the BridgeRequested event definition to verify available fields
ast-grep --lang solidity --pattern 'event BridgeRequested(
  $$$
)'

Length of output: 186


Script:

#!/bin/bash
# Search for the BridgeRequested event definition in Solidity files
rg 'event BridgeRequested\(' --glob '*.sol'

Length of output: 648


Script:

#!/bin/bash
# Extract full definitions of the BridgeRequested event in Solidity files
rg -A 10 'event BridgeRequested\(' --glob '*.sol'

Length of output: 6623

Copy link

This PR is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added Stale and removed Stale labels Oct 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
go Pull requests that update Go code needs-go-generate-services/rfq size/m
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant