From 36e70c545bef7a3a826530b2fbe45232aa28e0be Mon Sep 17 00:00:00 2001 From: racytech <82003208+racytech@users.noreply.github.com> Date: Mon, 8 May 2023 11:20:10 +0600 Subject: [PATCH] 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. --- cmd/rpcdaemon/commands/eth_receipts.go | 6 +++- consensus/misc/eip4844.go | 40 +++++++++++++++++------- consensus/misc/eip4844_test.go | 12 ++++--- core/error.go | 8 +++++ core/state_transition.go | 43 +++++++++++++++++++++++++- 5 files changed, 92 insertions(+), 17 deletions(-) diff --git a/cmd/rpcdaemon/commands/eth_receipts.go b/cmd/rpcdaemon/commands/eth_receipts.go index e1efb51b8..3a49ce3e9 100644 --- a/cmd/rpcdaemon/commands/eth_receipts.go +++ b/cmd/rpcdaemon/commands/eth_receipts.go @@ -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) } } diff --git a/consensus/misc/eip4844.go b/consensus/misc/eip4844.go index 84a649ff8..bcdf9066e 100644 --- a/consensus/misc/eip4844.go +++ b/consensus/misc/eip4844.go @@ -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 { diff --git a/consensus/misc/eip4844_test.go b/consensus/misc/eip4844_test.go index a703ea0ed..479082f57 100644 --- a/consensus/misc/eip4844_test.go +++ b/consensus/misc/eip4844_test.go @@ -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) } } diff --git a/core/error.go b/core/error.go index 82782407d..0ce38dbbc 100644 --- a/core/error.go +++ b/core/error.go @@ -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 diff --git a/core/state_transition.go b/core/state_transition.go index b1d2302b6..5102fb44b 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -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) }