eip-4844: data gas fees & related check (#7449)

Logic to compute fees for data blobs as well as additional check that
verifies if user was willing to pay the current `data_gas` price.
Updated `FakeExponential` function to work with uint256.
This commit is contained in:
racytech 2023-05-08 11:20:10 +06:00 committed by GitHub
parent 5a56b45b40
commit 36e70c545b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 92 additions and 17 deletions

View File

@ -791,7 +791,11 @@ func marshalReceipt(receipt *types.Receipt, txn types.Transaction, chainConfig *
if excessDataGas == nil {
log.Warn("excess data gas not set when trying to marshal blob tx")
} else {
fields["dataGasPrice"] = misc.GetDataGasPrice(excessDataGas)
dataGasPrice, err := misc.GetDataGasPrice(excessDataGas)
if err != nil {
log.Error(err.Error())
}
fields["dataGasPrice"] = dataGasPrice
fields["dataGasUsed"] = misc.GetDataGasUsed(numBlobs)
}
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
"math/big"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/chain"
"github.com/ledgerwatch/erigon/core/types"
@ -45,16 +46,33 @@ func CalcExcessDataGas(parentExcessDataGas *big.Int, newBlobs int) *big.Int {
// FakeExponential approximates factor * e ** (num / denom) using a taylor expansion
// as described in the EIP-4844 spec.
func FakeExponential(factor, num, denom *big.Int) *big.Int {
output := new(big.Int)
numAccum := new(big.Int).Mul(factor, denom)
for i := 1; numAccum.Sign() > 0; i++ {
output.Add(output, numAccum)
numAccum.Mul(numAccum, num)
iBig := big.NewInt(int64(i))
numAccum.Div(numAccum, iBig.Mul(iBig, denom))
func FakeExponential(factor, denom *uint256.Int, edg *big.Int) (*uint256.Int, error) {
numerator, overflow := uint256.FromBig(edg)
if overflow {
return nil, fmt.Errorf("FakeExponential: overflow converting excessDataGas: %v", edg)
}
return output.Div(output, denom)
output := uint256.NewInt(0)
numeratorAccum := new(uint256.Int)
_, overflow = numeratorAccum.MulOverflow(factor, denom)
if overflow {
return nil, fmt.Errorf("FakeExponential: overflow in MulOverflow(factor=%v, denom=%v)", factor, denom)
}
divisor := new(uint256.Int)
for i := 1; numeratorAccum.Sign() > 0; i++ {
_, overflow = output.AddOverflow(output, numeratorAccum)
if overflow {
return nil, fmt.Errorf("FakeExponential: overflow in AddOverflow(output=%v, numeratorAccum=%v)", output, numeratorAccum)
}
_, overflow = divisor.MulOverflow(denom, uint256.NewInt(uint64(i)))
if overflow {
return nil, fmt.Errorf("FakeExponential: overflow in MulOverflow(denom=%v, i=%v)", denom, i)
}
_, overflow = numeratorAccum.MulDivOverflow(numeratorAccum, numerator, divisor)
if overflow {
return nil, fmt.Errorf("FakeExponential: overflow in MulDivOverflow(numeratorAccum=%v, numerator=%v, divisor=%v)", numeratorAccum, numerator, divisor)
}
}
return output.Div(output, denom), nil
}
// CountBlobs returns the number of blob transactions in txs
@ -75,8 +93,8 @@ func VerifyEip4844Header(config *chain.Config, parent, header *types.Header) err
}
// GetDataGasPrice implements get_data_gas_price from EIP-4844
func GetDataGasPrice(excessDataGas *big.Int) *big.Int {
return FakeExponential(big.NewInt(params.MinDataGasPrice), excessDataGas, big.NewInt(params.DataGasPriceUpdateFraction))
func GetDataGasPrice(excessDataGas *big.Int) (*uint256.Int, error) {
return FakeExponential(uint256.NewInt(params.MinDataGasPrice), uint256.NewInt(params.DataGasPriceUpdateFraction), excessDataGas)
}
func GetDataGasUsed(numBlobs int) uint64 {

View File

@ -20,6 +20,7 @@ import (
"math/big"
"testing"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon/params"
)
@ -46,12 +47,15 @@ func TestFakeExponential(t *testing.T) {
}
for _, tt := range tests {
factor := big.NewInt(tt.factor)
factor := uint256.NewInt(uint64(tt.factor))
num := big.NewInt(tt.num)
denom := big.NewInt(tt.denom)
result := FakeExponential(factor, num, denom)
denom := uint256.NewInt(uint64(tt.denom))
result, err := FakeExponential(factor, denom, num)
if err != nil {
t.Error(err)
}
//t.Logf("%v*e^(%v/%v): %v", factor, num, denom, result)
if tt.want != result.Int64() {
if tt.want != result.ToBig().Int64() {
t.Errorf("got %v want %v", result, tt.want)
}
}

View File

@ -36,6 +36,10 @@ var (
// transaction with a tip higher than the total fee cap.
ErrTipAboveFeeCap = errors.New("tip higher than fee cap")
// ErrMaxFeePerDataGas is returned if the transaction specified a
// max_fee_per_data_gas that is below the current data gas price.
ErrMaxFeePerDataGas = errors.New("max fee per data gas too low")
// ErrTipVeryHigh is a sanity error to avoid extremely big numbers specified
// in the tip field.
ErrTipVeryHigh = errors.New("tip higher than 2^256-1")
@ -43,6 +47,10 @@ var (
// ErrFeeCapVeryHigh is a sanity error to avoid extremely big numbers specified
// in the fee cap field.
ErrFeeCapVeryHigh = errors.New("fee cap higher than 2^256-1")
// ErrInternalFailure is returned when an unexpected internal error condition
// prevents execution.
ErrInternalFailure = errors.New("internal failure")
)
// List of evm-call-message pre-checking errors. All state transition messages will

View File

@ -201,6 +201,25 @@ func (st *StateTransition) buyGas(gasBailout bool) error {
if overflow {
return fmt.Errorf("%w: address %v", ErrInsufficientFunds, st.msg.From().Hex())
}
// compute data fee for eip-4844 data blobs if any
dgval := new(uint256.Int)
var dataGasUsed uint64
if st.evm.ChainRules().IsCancun {
dataGasUsed = st.dataGasUsed()
if st.evm.Context().ExcessDataGas == nil {
return fmt.Errorf("%w: sharding is active but ExcessDataGas is nil", ErrInternalFailure)
}
dataGasPrice, err := misc.GetDataGasPrice(st.evm.Context().ExcessDataGas)
if err != nil {
return err
}
_, overflow = dgval.MulOverflow(dataGasPrice, new(uint256.Int).SetUint64(dataGasUsed))
if overflow {
return fmt.Errorf("%w: overflow converting datagas: %v", ErrInsufficientFunds, dgval)
}
}
balanceCheck := mgval
if st.gasFeeCap != nil {
balanceCheck = st.sharedBuyGasBalance.SetUint64(st.msg.Gas())
@ -212,6 +231,10 @@ func (st *StateTransition) buyGas(gasBailout bool) error {
if overflow {
return fmt.Errorf("%w: address %v", ErrInsufficientFunds, st.msg.From().Hex())
}
balanceCheck, overflow = balanceCheck.AddOverflow(balanceCheck, dgval)
if overflow {
return fmt.Errorf("%w: address %v", ErrInsufficientFunds, st.msg.From().Hex())
}
}
var subBalance = false
if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 {
@ -227,10 +250,15 @@ func (st *StateTransition) buyGas(gasBailout bool) error {
}
}
st.gas += st.msg.Gas()
st.initialGas = st.msg.Gas()
if err := st.gp.SubDataGas(dataGasUsed); err != nil {
return err
}
if subBalance {
st.state.SubBalance(st.msg.From(), mgval)
st.state.SubBalance(st.msg.From(), dgval)
}
return nil
}
@ -282,6 +310,19 @@ func (st *StateTransition) preCheck(gasBailout bool) error {
}
}
}
if st.dataGasUsed() > 0 && st.evm.ChainRules().IsCancun {
dataGasPrice, err := misc.GetDataGasPrice(st.evm.Context().ExcessDataGas)
if err != nil {
return err
}
maxFeePerDataGas := st.msg.MaxFeePerDataGas()
if dataGasPrice.Cmp(maxFeePerDataGas) > 0 {
return fmt.Errorf("%w: address %v, maxFeePerDataGas: %v dataGasPrice: %v, excessDataGas: %v",
ErrMaxFeePerDataGas,
st.msg.From().Hex(), st.msg.MaxFeePerDataGas(), dataGasPrice, st.evm.Context().ExcessDataGas)
}
}
return st.buyGas(gasBailout)
}