-
Notifications
You must be signed in to change notification settings - Fork 375
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: protocol level dynamic gas price #2544
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2544 +/- ##
==========================================
+ Coverage 60.14% 60.19% +0.04%
==========================================
Files 560 560
Lines 74738 75128 +390
==========================================
+ Hits 44950 45222 +272
- Misses 26400 26485 +85
- Partials 3388 3421 +33
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made some suggestions and asked a few questions. Looks mostly good to me, but I'm not the most familiar with the tm2 code.
// PriceChangeCompressor is to reduce the impact of gas changes to gas price. | ||
PriceChangeCompressor int64 = 8 // 1/8 = 0.125 | ||
|
||
// BlockTimeIotaMS is the block time iota (in ms) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update comment
@@ -623,6 +623,9 @@ var ( | |||
reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, reDnmString)) | |||
) | |||
|
|||
// export validateDenom | |||
var ValidateDenom func(string) error = validateDenom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is probably done to reduce the number of changes, but there are only five other places this function is called, so might as well just export it directly.
@@ -145,9 +145,15 @@ func execGenerate(cfg *generateCfg, io commands.IO) error { | |||
|
|||
// getDefaultGenesis returns the default genesis config | |||
func getDefaultGenesis() *types.GenesisDoc { | |||
bp := types.DefaultBlockParams() | |||
bp.InitialGasPriceAmount = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this set to zero? I notice this happens in most of the cases after types.DefaultBlockParams
is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to name this file gas_price.go
.
@@ -379,43 +379,54 @@ func DeductFees(bank BankKeeperI, ctx sdk.Context, acc std.Account, fees std.Coi | |||
// consensus. | |||
func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result { | |||
minGasPrices := ctx.MinGasPrices() | |||
blockHeader := ctx.BlockHeader().(*bft.Header) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to make this tm2 package to be useful for others in the future, I'm not sure this should be doing a type assertion on this interface. Instead could the BlockHeader
interface be modified to include a method that returns this gas price data?
|
||
// it calculates the minGasPrice for the txs to be included in the next block. | ||
// minGasPrice = lastPrice + lastPrice*(gasUsed-TargetBlockGas)/TargetBlockGas/GasCompressor) | ||
func (app *BaseApp) calBlockMinPriceAmount(gasUsed int64, lastPrice int64) int64 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you name this calcBlockMinPriceAmount
instead?
num = max(num, big.NewInt(minGasPrice)) | ||
} | ||
|
||
if !num.IsInt64() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a bunch of questions about this.
- Should this really panic here?
- Would this halt the chain?
- Can it be set to the max int64 value in this case?
- Is there an attack vector that could easily exploit this?
c := app.consensusParams.Block.PriceChangeCompressor | ||
|
||
lastMinPrice := big.NewInt(lastPrice) | ||
if gasUsed > targetGas { // increase gas price |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this calculation be done in a way that makes it a bit easier to follow based on the formula definition? Something like this?
// Declare big integers for the calculation.
minGasPriceBig := big.NewInt(0) // result value
lastPriceBig := big.NewInt(lastPrice)
gasUsedBig := big.NewInt(gasUsed)
targetGasBig := big.NewInt(targetGas)
gasCompressorBig := big.NewInt(app.consensusParams.Block.PriceChangeCompressor)
// Formula:
// minGasPrice = lastPrice + lastPrice*(gasUsed-TargetBlockGas)/TargetBlockGas/GasCompressor)
minGasPriceBig.Sub(gasUsedBig, targetGasBig)
minGasPriceBig.Mul(minGasPriceBig, lastPriceBig)
minGasPriceBig.Div(minGasPriceBig, targetGasBig)
minGasPriceBig.Div(minGasPriceBig, gasCompressorBig)
minGasPriceBig.Add(minGasPriceBig, lastPriceBig)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The math formula is just an abstraction of a simple solution for the underlying problem we're trying to solve.
- What do we do if the gas used is less than the target gas in a block?
- How do we bring the gas used back to the target level, if gas used is more than the target?
We simplify the solution with a one-line formula to explain the idea. However, in reality, we need to treat two scenarios differently. For example, in the first case, we need to increase the gas by at least 1 unit, instead of round down for the integer divisions, and in the second case, we should set a floor as the target gas price.
This is just a starting point. Down the line, the solution might not be even representable by one simple formula
PriceChangeCompressor int64 // must be >0 | ||
InitialGasPriceAmount int64 // must be >=0 | ||
InitialGasPriceDenom string // must not be "" | ||
InitialGasPriceGas int64 // must be >=1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to remove; instead add these to GnoGenesisState.Auth.
BREAKING CHANGE: addition block header, state value and block parameters are added. It may fail on playing back the blocks produced in previous implementation.
Main idea
This is Inspired by EIP 1559, it adjusts gas price based on ratio of gas used last bock and the target block gas.
the compressor is used to reduce the impact on price due to sudden changes on
When the last gas used in a block is above the target gas, we increase the gas price
When the last gas used in a block is below the target gas, we decrease the gas price until it returns to the initial gas price in the block.
Impact
The Cosmos SDK has an optional setting for a minimum gas price. Each validator can configure their own values to only accept transactions with a gas price that meets their setting in the mempool. When a user submits a transaction on-chain, the gas price is calculated as gas-fee / gas-wanted.
With the addition of the block gas price, a network-wide minimum gas price is enforced for every validator. Users will need to provide a gas price that meets the requirements set by both the validator and the network.
Implementation flow
Play back block and Commit block:
The consensus engine sends the last gas price in the block header and block parameters to the app during InitChain().
In EndBlock(), the base app calculates the updated gas price using the formula, updates the value in the header, and sends the header back to the consensus engine.
Accept transactions in the mempool
Changes
This is the initial implementation, we need more thorough testing and cross checking on different spamming scenarios
Contributors' checklist...
BREAKING CHANGE: xxx
message was included in the description