cip | title | author | discussions-to | status | type | category | created | license |
---|---|---|---|---|---|---|---|---|
61 |
Restore BaseFee opcode/header field |
Gastón Ponti (@gastonponti) |
Final |
Standards Track |
Ring 0 |
2023-06-21 |
Apache 2.0 |
Move the minimumGasPrice
of Celo from our core contract to the header of the block, and activate the EIP-3198 (add the baseFee
opcode)
This CIP proposes to remove the minimumGasPrice
update made by our consensus layer at the end of each block, to be calculated at the beginning of every block proposed, and storing it in the header. Also, the implementation of the EIP-3198 which adds the baseFee
opcode to the EVM.
This CIP aims to reduce the complexity of our consensus layer, and get us closer to the Ethereum’s implementation of the EIP-1559. This will facilitate future upstream merges and the plans to separate our execution and consensus layers.
blockGasLimit
is a governable parameter from the BlockchainParametersContract that represents the gas limit of the blockblockGasTotal
is the gas used for a specific blockblockDensity
is a number in the interval[0, 1]
that represents the usage rate of the block ⇒blockGasTotal / blockGasLimit
adjustmentSpeed
is a governable parameter from the GasPriceMinimumContract that represents the speed of gas price minimum adjustment due to congestiontargetDensity
is a governable parameter from the GasPriceMinimumContract that represents the block congestion level targeted by the gas price minimum calculationgasPriceMinimumFloor
is a governable parameter from the GasPriceMinimumContract that represents the minimum gas price threshold
Add the baseFee
field to the header structure.
Implement the EIP-3198 that adds the BASEFEE (0x48)
opcode.
Switch the way we are storing the gasPriceMinimum
(requires to be enabled during a hardfork)
Currently the GasPriceMinimum
is modified and saved calling the GasPriceMinimum
contract in the finalize stage of the consensus here. This must be avoided from the Hardfork Block.
Then, in the prepareBlock
function (here) will need to
-
For the G_HARDFORK_BLOCK:
Retrieve the
gasPriceMinimum
of the actual state (which was updated at the end of theG_HARDFORK_BLOCK - 1
) and add that number as thebaseFee
of the header -
From the
G_HARDFORK_BLOCK + 1
:The client must call the
getUpdatedGasPriceMinimum
of the modifiedgasPriceMinimum
contract with the state of the parent block, to retrieve the newbaseFee
Before the hardfork, we will need to deploy a new version of the GasPriceMinimum
contract with the following changes:
-
Rename the
gasPriceMinimum
variable todeprecated_gasPriceMinimum
This step is required to be able to define a new function
gasPriceMinimum
that will return the renamed variabledeprecated_gasPriceMinimum
or theblock.basefee
depending on theG_HARDFORK_BLOCK
-
Change the way the
gasPriceMinimum
getter was defined and override it:gasPriceMinimum() view returns (uint256) { if (G_HARDFORK_BLOCK > 0 && block.number >= G_HARDFORK_BLOCK) { return block.baseFee; } else { return gasPriceMinimum; } }
-
Change the implementation of
getGasPriceMinimum
(line 116) to:function getGasPriceMinimum(address tokenAddress) external view returns (uint256) { if ( tokenAddress == address(0) || tokenAddress == registry.getAddressForOrDie(GOLD_TOKEN_REGISTRY_ID) ) { return gasPriceMinimum(); } else { ISortedOracles sortedOracles = ISortedOracles( registry.getAddressForOrDie(SORTED_ORACLES_REGISTRY_ID) ); uint256 rateNumerator; uint256 rateDenominator; (rateNumerator, rateDenominator) = sortedOracles.medianRate(tokenAddress); return (gasPriceMinimum().mul(rateNumerator).div(rateDenominator)); } }
-
Update the
getUpdatedGasPriceMinimum
function to maintain compatibilityfunction getUpdatedGasPriceMinimum(uint256 blockGasTotal, uint256 blockGasLimit) public view returns (uint256) { FixidityLib.Fraction memory blockDensity = FixidityLib.newFixedFraction( blockGasTotal, blockGasLimit ); bool densityGreaterThanTarget = blockDensity.gt(targetDensity); FixidityLib.Fraction memory densityDelta = densityGreaterThanTarget ? blockDensity.subtract(targetDensity) : targetDensity.subtract(blockDensity); FixidityLib.Fraction memory adjustment = densityGreaterThanTarget ? FixidityLib.fixed1().add(adjustmentSpeed.multiply(densityDelta)) : FixidityLib.fixed1().subtract(adjustmentSpeed.multiply(densityDelta)); uint256 newGasPriceMinimum = adjustment .multiply(FixidityLib.newFixed(gasPriceMinimum())) // This line .add(FixidityLib.fixed1()) .fromFixed(); return newGasPriceMinimum >= gasPriceMinimumFloor ? newGasPriceMinimum : gasPriceMinimumFloor; }
-
The
updateGasPriceMinimum
is only called by the VM, and must not call this function after the HardFork. There is no need to change it other than having a cleaner contract.
The decision of moving the gasPriceMinimum
to the header as the baseFee
gives us a few benefits:
- It will bring us closer to upstream, what will increase our compatibility with Ethereum, not only from our node perspective (it will simplify our client), but also for the tools of the ecosystem
- It will simplify our consensus layer. Avoiding the update of the
gasPriceMinimum
variable in our core contract, it won’t modify the final state of the block, thus it won’t generate a block receipt, which is something we want to reduce as much as possible - The contract’s developers, will be able to use an opcode instead of a contract call to know the
gasPriceMinimum
of the block
This needs to be added as part of a fork. To maintain full backwards compatibility, we will also need to re deploy the GasPriceMinimum
contract consuming the baseFee
opcode.
The event GasPriceMinimumUpdated
won't be emitted anymore after the fork. This is due to removing the logic of updating the state of the contract in our consensus. Adding a transaction to just emit that event, goes against the idea of this CIP of simplifying our consensus.
As the information of the baseFee
is not in the state anymore, checking that a change was performed, has been simplified (Header(N).baseFeePerGas != Header(N-1).baseFeePerGas
).
The datastructure that is passed into keccak256 to calculate the block hash is changing, and all applications that are validating blocks are valid or using the block hash to verify block contents will need to be adapted to support the new datastructure (one additional item). If you only take the block header bytes and hash them you should still correctly get a hash, but if you construct a block header from its constituent elements you will need to add in the new one at the end.
Assuming current block base fee is 7 wei
. This should push the value 7
(left padded byte32) to the stack.
Bytecode: 0x4800
(BASEFEE, STOP
)
Pc | Op | Cost | Stack | RStack |
---|---|---|---|---|
0 | BASEFEE | 2 | [] | [] |
1 | STOP | 0 | [7] | [] |
Output: 0x
Consumed gas: 2
Contract:
Client:
Just as a consideration, even if it won’t be a big change, this CIP adds a field to the header which will increase its size.
This work is licensed under the Apache License, Version 2.0.