Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: implement the gas estimation API #4257

Merged
merged 23 commits into from
Jan 23, 2025
Merged

feat: implement the gas estimation API #4257

merged 23 commits into from
Jan 23, 2025

Conversation

rach-id
Copy link
Member

@rach-id rach-id commented Jan 22, 2025

Overview

Implements the gas estimation endpoint following: https://github.com/celestiaorg/CIPs/blob/main/cips/cip-18.md

Is there an issue for this or I create one?

@rach-id rach-id added the WS: BestTxs Reliable and seamless transaction flow label Jan 22, 2025
@rach-id rach-id self-assigned this Jan 22, 2025
@rach-id rach-id requested a review from a team as a code owner January 22, 2025 13:51
@rach-id rach-id requested review from cmwaters and ninabarbakadze and removed request for a team January 22, 2025 13:51
@rach-id rach-id requested a review from vgonkivs January 22, 2025 13:52
Comment on lines 82 to 85
// We'll do a tolerance check for floating-point comparisons.
if math.Abs(got-tt.want) > 1e-9 {
t.Errorf("stdDev(%v) = %v, want %v", tt.gasPrices, got, tt.want)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

this is chatgpt's doing and I think it's cool. we can remove if it looks complicated :D

Copy link
Contributor

coderabbitai bot commented Jan 22, 2025

📝 Walkthrough

Walkthrough

This pull request introduces enhancements to the gas estimation functionality within the Celestia blockchain application. Key changes include the addition of a gRPC service for estimating gas prices and usage, the implementation of statistical methods for gas price calculations, and the creation of corresponding test cases. The Makefile has been modified to exclude specific tests related to gas estimation. The Architecture Decision Record (ADR) for this feature has been updated from "Proposed" to "Implemented".

Changes

File Change Summary
Makefile Updated test-race target to include additional test exclusions: TestEstimateGasUsed, TestEstimateGasPrice
app/app.go Added import for gasestimation package and registered gas estimation service with gRPC router
app/grpc/gasestimation/gas_estimator.go Implemented gas estimation service with methods for estimating gas price and usage
app/grpc/gasestimation/gas_estimator_test.go Added unit tests for gas estimation utility functions: TestMean, TestStandardDeviation, TestEstimateGasPriceForTransactions
app/test/gas_estimation_test.go New integration tests for gas price estimation: TestEstimateGasPrice, TestEstimateGasUsed
buf.yaml Added build section specifying roots and excludes for proto files
docs/architecture/adr-023-gas-used-and-gas-price-estimation.md Updated status from "Proposed" to "Implemented"
proto/celestia/core/v1/gas_estimation/gas_estimator.proto Added proto definitions for gas estimation service, including RPC methods and message structures

Sequence Diagram

sequenceDiagram
    participant Client
    participant GasEstimationService
    participant Blockchain

    Client->>GasEstimationService: EstimateGasPrice(request)
    GasEstimationService->>Blockchain: Fetch recent transactions
    Blockchain-->>GasEstimationService: Transaction data
    GasEstimationService->>GasEstimationService: Calculate gas price
    GasEstimationService-->>Client: Return estimated gas price
Loading

Possibly related PRs

  • feat: add auto gas and fee estimations #3510: Enhancements to gas estimation processes, which are directly related to the modifications made in the main PR regarding the exclusion of specific tests related to gas estimation functionalities.
  • docs: gas used and gas price estimation ADR #4238: Documentation of the architecture decision regarding gas used and gas price estimation, which aligns with the enhancements made in the main PR that involve testing and functionality related to gas estimation.

Suggested labels

documentation

Suggested reviewers

  • cmwaters
  • ninabarbakadze
  • rootulp
  • evan-forbes
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

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:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • 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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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.

rootulp
rootulp previously approved these changes Jan 22, 2025
Copy link
Collaborator

@rootulp rootulp left a comment

Choose a reason for hiding this comment

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

LGTM.

One suggestion is to use medians like was originally stated in the ADR (median of top 10%, median, median of bottom 10%).

If we use averages and z-scores, then we should confirm that the gas prices in historical blocks on Mainnet follow a normal distribution.

Comment on lines +19 to +21
// EstimationZScore is the z-score corresponding to 10% and 90% of the gas prices distribution.
// More information can be found in: https://en.wikipedia.org/wiki/Standard_normal_table#Cumulative_(less_than_Z)
const EstimationZScore = 1.28
Copy link
Collaborator

Choose a reason for hiding this comment

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

Before we use Z score, IMO we should verify that the gas prices in historical blocks form a normal distribution.

I tried to convey in the ADR that median of top 10% is likely easier and good enough so we don't need to use z scores.

Copy link
Member Author

Choose a reason for hiding this comment

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

Before we use Z score, IMO we should verify that the gas prices in historical blocks form a normal distribution.

Having a normal distribution is optional in our case IMO since we don't need spot on values. There are three cases:

  • a flat distribution: where the values will generally be close the to the mean which is fine
  • a U distribution (I mean just the shape of the distribution forming an U or a W or similar): where we have two cases: either the low prices will be higher than 10%. Or the high prices will be lower than 90%. but I guess that's fine.

I tried to convey in the ADR that median of top 10% is likely easier and good enough so we don't need to use z scores.

got you 👍 I avoided implementing the percentile because it means implementing a bit of complicated math method and maintaining it and I wanted to avoid that. If we can find a library we can use, that has an open source license, I'll definitely change this implementation.

app/grpc/gasestimation/gas_estimator.go Outdated Show resolved Hide resolved
app/grpc/gasestimation/gas_estimator.go Outdated Show resolved Hide resolved
@@ -1,15 +1,18 @@
# Generated by buf. DO NOT EDIT.
version: v1
version: v1beta1
Copy link
Collaborator

Choose a reason for hiding this comment

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

what motivated this change?

Copy link
Member Author

Choose a reason for hiding this comment

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

this is buf.lock. It gets updated automatically when you run buf dep update and it updated this for some reason :D

proto/celestia/core/v1/gas_estimation/gas_estimator.proto Outdated Show resolved Hide resolved
@evan-forbes evan-forbes linked an issue Jan 22, 2025 that may be closed by this pull request
@evan-forbes evan-forbes linked an issue Jan 22, 2025 that may be closed by this pull request
evan-forbes
evan-forbes previously approved these changes Jan 22, 2025
app/test/gas_estimation_test.go Show resolved Hide resolved
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: 5

🧹 Nitpick comments (3)
app/grpc/gasestimation/gas_estimator_test.go (2)

82-84: Adjust tolerance in floating-point comparison

In TestStandardDeviation, the tolerance for comparing floating-point numbers is very strict:

if math.Abs(got-tt.want) > 1e-9 {
    t.Errorf("stdDev(%v) = %v, want %v", tt.gasPrices, got, tt.want)
}

Consider increasing the tolerance to account for floating-point precision:

- if math.Abs(got-tt.want) > 1e-9 {
+ if math.Abs(got-tt.want) > 1e-7 {

130-136: Use appropriate assertion for floating-point equality

In TestEstimateGasPriceForTransactions, assert.Equal is used to compare floating-point numbers:

assert.Equal(t, tt.want, got)

Use assert.InDelta to allow for minor differences:

- assert.Equal(t, tt.want, got)
+ assert.InDelta(t, tt.want, got, 1e-7)
app/test/gas_estimation_test.go (1)

126-127: Use assert.InDelta for comparing floating-point values

In TestEstimateGasPrice, floating-point numbers are compared using string formatting:

assert.Equal(t, fmt.Sprintf("%.2f", tt.expectedGasPrice), fmt.Sprintf("%.2f", resp.EstimatedGasPrice))

Replace with assert.InDelta for better precision handling:

- assert.Equal(t, fmt.Sprintf("%.2f", tt.expectedGasPrice), fmt.Sprintf("%.2f", resp.EstimatedGasPrice))
+ assert.InDelta(t, tt.expectedGasPrice, resp.EstimatedGasPrice, 1e-2)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9de8ad3 and 141b281.

⛔ Files ignored due to path filters (2)
  • app/grpc/gasestimation/gas_estimator.pb.go is excluded by !**/*.pb.go
  • buf.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • Makefile (1 hunks)
  • app/app.go (2 hunks)
  • app/grpc/gasestimation/gas_estimator.go (1 hunks)
  • app/grpc/gasestimation/gas_estimator_test.go (1 hunks)
  • app/test/gas_estimation_test.go (1 hunks)
  • buf.yaml (1 hunks)
  • docs/architecture/adr-023-gas-used-and-gas-price-estimation.md (1 hunks)
  • proto/celestia/core/v1/gas_estimation/gas_estimator.proto (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • docs/architecture/adr-023-gas-used-and-gas-price-estimation.md
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: test / test
  • GitHub Check: Summary
🔇 Additional comments (9)
app/grpc/gasestimation/gas_estimator.go (2)

173-189: Ensure proper handling of empty gasPrices in estimation

The function estimateGasPriceForTransactions does not explicitly handle the case where gasPrices is empty. This could unintentionally return an estimated gas price of zero.

Confirm whether returning zero is acceptable when gasPrices is empty. If not, consider modifying the function to return the minimum gas price in such cases.


19-21: Reconsider use of Z-score without verifying distribution

Using Z-scores assumes that gas prices follow a normal distribution. If this assumption doesn't hold, the estimates might be inaccurate.

Consider verifying the distribution of gas prices. If they are not normally distributed, alternative methods like percentile calculations might be more appropriate.

app/test/gas_estimation_test.go (1)

168-169: Enhance test validity by avoiding circular dependencies

In TestEstimateGasUsed, the expected gas is obtained using txClient.EstimateGas, which relies on the same simulation logic as the method under test.

To make the test more meaningful, compare the estimated gas against hard-coded expected values or use known benchmark transactions.

app/app.go (1)

9-9: LGTM! Clean integration of the gas estimation service.

The changes correctly integrate the gas estimation service by adding the required import and registering the service with the gRPC query router.

Also applies to: 758-758

buf.yaml (1)

Line range hint 9-14: LGTM! Standard buf.build configuration.

The build configuration correctly specifies the proto roots and excludes the standard Google protobuf definitions.

proto/celestia/core/v1/gas_estimation/gas_estimator.proto (4)

1-8: LGTM! Clean protobuf setup.

The package declaration and imports are correctly structured.


9-28: LGTM! Well-documented service definition.

The GasEstimator service is clearly defined with comprehensive documentation for both RPC methods.


30-46: Improve documentation clarity for priority levels.

The documentation could be more precise about the statistical methods used for each priority level.

Apply this diff to improve the documentation:

-// - Medium Priority: The gas price is the mean of all gas prices from the last 5 blocks.
-// - Low Priority: The gas price is the value at the end of the lowest 10% of gas prices from the last 5 blocks.
+// - Medium Priority: The gas price is the mean of all gas prices from the last 5 blocks.
+// - Low Priority: The gas price is the value at the end of the lowest 10% of gas prices from the last 5 blocks.

48-71: LGTM! Clear message definitions.

The request and response message definitions are well-structured with appropriate field types.

app/grpc/gasestimation/gas_estimator.go Show resolved Hide resolved
Comment on lines 194 to 220
gasPrices := make([]float64, 0)
for _, tx := range txs {
var feeWithDenom string
for _, event := range tx.TxResult.Events {
if event.GetType() == "tx" {
for _, attr := range event.Attributes {
if string(attr.Key) == "fee" {
feeWithDenom = string(attr.Value)
}
}
}
}
if feeWithDenom == "" {
return nil, fmt.Errorf("couldn't find fee for transaction %s", tx.Hash)
}
feeWithoutDenom, found := strings.CutSuffix(feeWithDenom, appconsts.BondDenom)
if !found {
return nil, fmt.Errorf("couldn't find fee denom for transaction %s: %s", tx.Hash, feeWithDenom)
}
fee, err := strconv.ParseFloat(feeWithoutDenom, 64)
if err != nil {
return nil, fmt.Errorf("couldn't parse fee for transaction %s: %w", tx.Hash, err)
}
gasPrices = append(gasPrices, fee/float64(tx.TxResult.GasWanted))
}
return gasPrices, 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 division by zero in gas price calculation

In the extractGasPriceFromTransactions function, there's a risk of dividing by zero if tx.TxResult.GasWanted is zero:

gasPrices = append(gasPrices, fee/float64(tx.TxResult.GasWanted))

Add a check to prevent division by zero:

+ if tx.TxResult.GasWanted == 0 {
+     return nil, fmt.Errorf("GasWanted is zero for transaction %s", tx.Hash)
+ }
gasPrices = append(gasPrices, fee/float64(tx.TxResult.GasWanted))

Copy link
Member Author

Choose a reason for hiding this comment

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

app/grpc/gasestimation/gas_estimator.go Show resolved Hide resolved
app/grpc/gasestimation/gas_estimator.go Show resolved Hide resolved
Makefile Show resolved Hide resolved
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

♻️ Duplicate comments (2)
app/grpc/gasestimation/gas_estimator.go (2)

87-89: ⚠️ Potential issue

Fix error handling for UnmarshalBlobTx

The error check should be independent of isBlob. If UnmarshalBlobTx returns an error, it should be handled regardless of the blob status.

-if isBlob && err != nil {
+if err != nil {
    return nil, err
}

168-172: ⚠️ Potential issue

Update comments to match implementation

The comments describe percentile-based calculations, but the implementation uses mean and standard deviation.

-// - High Priority: The gas price is the price at the start of the top 10% of transactions' gas prices from the last five blocks.
-// - Medium Priority: The gas price is the mean of all gas prices from the last five blocks.
-// - Low Priority: The gas price is the value at the end of the lowest 10% of gas prices from the last five blocks.
-// - Unspecified Priority (default): This is equivalent to the Medium priority, using the mean of all gas prices from the last five blocks.
+// - High Priority: The gas price is the mean plus EstimationZScore times the standard deviation.
+// - Medium Priority: The gas price is the mean of all gas prices from the last five blocks.
+// - Low Priority: The gas price is the mean minus EstimationZScore times the standard deviation.
+// - Unspecified Priority (default): Equivalent to Medium priority.
🧹 Nitpick comments (2)
app/grpc/gasestimation/gas_estimator.go (2)

61-61: Convert variable to constant

The numberOfTransactionsPerPage should be declared as a constant since it represents a fixed limit.

-var numberOfTransactionsPerPage = 100
+const numberOfTransactionsPerPage = 100

108-163: Consider caching gas price estimates

The estimateGasPrice method queries the last five blocks for every request. Consider implementing a caching mechanism with a short TTL (e.g., 1 block) to reduce RPC calls and improve performance.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 141b281 and 5946b51.

📒 Files selected for processing (1)
  • app/grpc/gasestimation/gas_estimator.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Summary
🔇 Additional comments (1)
app/grpc/gasestimation/gas_estimator.go (1)

217-219: LGTM! Good error handling

Proper check for division by zero has been implemented.

Copy link
Collaborator

@rootulp rootulp left a comment

Choose a reason for hiding this comment

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

Great work shipping this so quickly!

My question about do historical blocks have gas prices that follow a normal distribution shouldn't block merging this

@rach-id rach-id merged commit 02f04c9 into main Jan 23, 2025
28 checks passed
@rach-id rach-id deleted the gas-price-estimation branch January 23, 2025 16:33
rach-id added a commit that referenced this pull request Jan 27, 2025
## Overview

Implements the gas estimation endpoint following:
https://github.com/celestiaorg/CIPs/blob/main/cips/cip-18.md

Is there an issue for this or I create one?

---------

Co-authored-by: Rootul P <[email protected]>
(cherry picked from commit 02f04c9)
rach-id added a commit that referenced this pull request Jan 27, 2025
## Overview

Implements the gas estimation endpoint following:
https://github.com/celestiaorg/CIPs/blob/main/cips/cip-18.md

Is there an issue for this or I create one?

---------

Co-authored-by: Rootul P <[email protected]>
(cherry picked from commit 02f04c9)
rach-id added a commit that referenced this pull request Jan 27, 2025
## Overview

Implements the gas estimation endpoint following:
https://github.com/celestiaorg/CIPs/blob/main/cips/cip-18.md

Is there an issue for this or I create one?

---------

Co-authored-by: Rootul P <[email protected]>
(cherry picked from commit 02f04c9)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
WS: BestTxs Reliable and seamless transaction flow
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support the gas estimator interface as part of CIP 18
3 participants