Skip to content

Commit

Permalink
handle data gas / data gas fees appropriately (ethereum#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
roberto-bayardo authored Oct 7, 2022
1 parent 619c609 commit 28e8789
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 43 deletions.
1 change: 1 addition & 0 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ func (m callMsg) To() *common.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
func (m callMsg) MaxFeePerDataGas() *big.Int { return m.CallMsg.MaxFeePerDataGas }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
Expand Down
5 changes: 5 additions & 0 deletions consensus/misc/eip4844.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,8 @@ func VerifyEip4844Header(config *params.ChainConfig, parent, header *types.Heade
}
return nil
}

// 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))
}
2 changes: 2 additions & 0 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3923,6 +3923,8 @@ func TestDataBlobTxs(t *testing.T) {
msg.Nonce = view.Uint64View(0)
msg.GasFeeCap.SetFromBig(newGwei(5))
msg.GasTipCap.SetFromBig(big.NewInt(2))
msg.MaxFeePerDataGas.SetFromBig(big.NewInt(params.MinDataGasPrice))
// TODO: Add test case for max data fee too low
msg.BlobVersionedHashes = []common.Hash{one, two}
txdata := &types.SignedBlobTx{Message: msg}

Expand Down
4 changes: 4 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ var (
// transaction with a tip higher than the total fee cap.
ErrTipAboveFeeCap = errors.New("max priority fee per gas higher than max fee per gas")

// 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 data fee per gas too low")

// ErrTipVeryHigh is a sanity error to avoid extremely big numbers specified
// in the tip field.
ErrTipVeryHigh = errors.New("max priority fee per gas higher than 2^256-1")
Expand Down
73 changes: 52 additions & 21 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (

"github.com/ethereum/go-ethereum/common"
cmath "github.com/ethereum/go-ethereum/common/math"
// "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -72,6 +72,7 @@ type Message interface {
GasPrice() *big.Int
GasFeeCap() *big.Int
GasTipCap() *big.Int
MaxFeePerDataGas() *big.Int
Gas() uint64
Value() *big.Int

Expand Down Expand Up @@ -162,25 +163,22 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas
}
// TODO
//if rules.EIP4844 {
//gas += uint64(blobCount) * getBlobGas(blockExcessBlobs)
//}
return gas, nil
}

// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp,
evm: evm,
msg: msg,
gasPrice: msg.GasPrice(),
gasFeeCap: msg.GasFeeCap(),
gasTipCap: msg.GasTipCap(),
value: msg.Value(),
data: msg.Data(),
state: evm.StateDB,
gp: gp,
evm: evm,
msg: msg,
gasPrice: msg.GasPrice(),
gasFeeCap: msg.GasFeeCap(),
gasTipCap: msg.GasTipCap(),
maxFeePerDataGas: msg.MaxFeePerDataGas(),
value: msg.Value(),
data: msg.Data(),
state: evm.StateDB,
}
}

Expand All @@ -206,21 +204,35 @@ func (st *StateTransition) to() common.Address {
func (st *StateTransition) buyGas() error {
mgval := new(big.Int).SetUint64(st.msg.Gas())
mgval = mgval.Mul(mgval, st.gasPrice)
balanceCheck := mgval
if st.gasFeeCap != nil {
balanceCheck = new(big.Int).SetUint64(st.msg.Gas())
balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap)
balanceCheck.Add(balanceCheck, st.value)

dgval := new(big.Int)
if st.evm.ChainConfig().IsSharding(st.evm.Context.BlockNumber) {
// add in fee for eip-4844 data blobs if any
dgval.Mul(misc.GetDataGasPrice(st.evm.Context.ExcessDataGas), st.dataGasUsed())
}

balanceCheck := new(big.Int)
if st.gasFeeCap == nil {
balanceCheck.Set(mgval)
} else {
balanceCheck.Add(st.value, dgval)
// EIP-1559 mandates that the sender has enough balance to cover not just actual fee but
// the max gas fee, so we compute this upper bound rather than use mgval here.
maxGasFee := new(big.Int).SetUint64(st.msg.Gas())
maxGasFee.Mul(maxGasFee, st.gasFeeCap)
balanceCheck.Add(balanceCheck, maxGasFee)
}

if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
}
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
return err
}
st.gas += st.msg.Gas()

st.initialGas = st.msg.Gas()

mgval.Add(mgval, dgval) // both regular gas fee and data gas fee need to be deducted
st.state.SubBalance(st.msg.From(), mgval)
return nil
}
Expand Down Expand Up @@ -270,6 +282,13 @@ func (st *StateTransition) preCheck() error {
}
}
}
if st.evm.ChainConfig().IsSharding(st.evm.Context.BlockNumber) {
dataGasPrice := misc.GetDataGasPrice(st.evm.Context.ExcessDataGas)
if dataGasPrice.Cmp(st.maxFeePerDataGas) > 0 {
return fmt.Errorf("%w: address %v, maxFeePerDataGas: %v dataGasPrice: %v", ErrMaxFeePerDataGas,
st.msg.From().Hex(), st.maxFeePerDataGas, dataGasPrice)
}
}
return st.buyGas()
}

Expand All @@ -291,7 +310,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
// applying the message. The rules include these clauses
//
// 1. the nonce of the message caller is correct
// 2. caller has enough balance to cover transaction fee(gaslimit * gasprice)
// 2. caller has enough balance to cover:
// Legacy tx: fee(gaslimit * gasprice)
// EIP-1559 tx: tx.value + max-fee(gaslimit * gascap + datagas * datagasprice)
// 3. the amount of gas required is available in the block
// 4. the purchased gas is enough to cover intrinsic usage
// 5. there is no overflow when calculating intrinsic gas
Expand Down Expand Up @@ -402,3 +423,13 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
func (st *StateTransition) gasUsed() uint64 {
return st.initialGas - st.gas
}

func (st *StateTransition) dataGasUsed() *big.Int {
dataGas := new(big.Int)
l := int64(len(st.msg.DataHashes()))
if l != 0 {
dataGas.SetInt64(l)
dataGas.Mul(dataGas, big.NewInt(params.DataGasPerBlob))
}
return dataGas
}
2 changes: 1 addition & 1 deletion core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const (

// txWrapDataMax is the maximum size for the additional wrapper data,
// enough to encode a blob-transaction wrapper data (48 bytes for commitment, 4 for offset, 48 for a commitment)
txWrapDataMax = 4 + 4 + params.MaxBlobsPerTx*(params.FieldElementsPerBlob*32+48)
txWrapDataMax = 4 + 4 + params.MaxBlobsPerBlock*(params.FieldElementsPerBlob*32+48)
)

var (
Expand Down
2 changes: 1 addition & 1 deletion core/types/data_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ func (b *BlobTxWrapData) verifyVersionedHash(inner TxData) error {
if !ok {
return fmt.Errorf("expected signed blob tx, got %T", inner)
}
if a, b := len(blobTx.Message.BlobVersionedHashes), params.MaxBlobsPerTx; a > b {
if a, b := len(blobTx.Message.BlobVersionedHashes), params.MaxBlobsPerBlock; a > b {
return fmt.Errorf("too many blobs in blob tx, got %d, expected no more than %d", a, b)
}
if a, b := len(b.BlobKzgs), len(b.Blobs); a != b {
Expand Down
17 changes: 16 additions & 1 deletion core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)

Expand Down Expand Up @@ -403,13 +404,27 @@ func (tx *Transaction) To() *common.Address {
return copyAddressPtr(tx.inner.to())
}

// Cost returns gas * gasPrice + value.
// Cost returns (gas * gasPrice) + (DataGas() * maxDataFeePerGas) + value.
func (tx *Transaction) Cost() *big.Int {
total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
total.Add(total, tx.Value())
dataGasFee := tx.DataGas()
dataGasFee.Mul(dataGasFee, tx.MaxFeePerDataGas())
total.Add(total, dataGasFee)
return total
}

// DataGas implements get_total_data_gas from EIP-4844
func (tx *Transaction) DataGas() *big.Int {
r := new(big.Int)
l := int64(len(tx.DataHashes()))
if l != 0 {
r.SetInt64(l)
r.Mul(r, big.NewInt(params.DataGasPerBlob))
}
return r
}

// RawSignatureValues returns the V, R, S signature values of the transaction.
// The return values should not be modified by the caller.
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
Expand Down
3 changes: 1 addition & 2 deletions light/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
}

// Transactor should have enough funds to cover the costs
// cost == V + GP * GL
// cost == V + GP * GL + DGP * DG
if b := currentState.GetBalance(from); b.Cmp(tx.Cost()) < 0 {
return core.ErrInsufficientFunds
}
Expand All @@ -389,7 +389,6 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
EIP2028: pool.istanbul,
EIP4844: pool.eip4844,
}
// TODO: Check DataGas
gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, rules)
if err != nil {
return err
Expand Down
22 changes: 8 additions & 14 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,20 +158,14 @@ const (
RefundQuotient uint64 = 2
RefundQuotientEIP3529 uint64 = 5

// Fixed cost for sending a data blob.
BlobGas uint64 = 120000

MaxDataGasPerBlock = 1 << 21
DataGasPerBlob = 1 << 17
MaxDataBlobsPerBlock = MaxDataGasPerBlock / DataGasPerBlob

TargetDataGasPerBlock = 1 << 20

MaxBlobsPerTx = 2
MaxBlobsPerBlock = 16
TargetBlobsPerBlock = 8
FieldElementsPerBlob = 4096 // each field element is 32 bytes
GasPriceUpdateFractionPerBlob = 64
// stuff from EIP-4844
FieldElementsPerBlob = 4096 // each field element is 32 bytes
MaxDataGasPerBlock = 1 << 21
DataGasPerBlob = 1 << 17
TargetDataGasPerBlock = 1 << 20
MinDataGasPrice = 10e8
DataGasPriceUpdateFraction = 8902606
MaxBlobsPerBlock = MaxDataGasPerBlock / DataGasPerBlob

BlobVerificationGas uint64 = 1800000
BlobCommitmentVersionKZG uint8 = 0x01
Expand Down
6 changes: 3 additions & 3 deletions tests/kzg_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func BenchmarkVerifyBlobsWithoutKZGProof(b *testing.B) {
}

func BenchmarkVerifyBlobs(b *testing.B) {
blobs := make([]types.Blob, params.MaxBlobsPerTx)
blobs := make([]types.Blob, params.MaxBlobsPerBlock)
var commitments []types.KZGCommitment
var hashes []common.Hash
for i := 0; i < len(blobs); i++ {
Expand Down Expand Up @@ -139,7 +139,7 @@ func BenchmarkVerifyMultiple(b *testing.B) {
var blobs []types.Blob
var commitments []types.KZGCommitment
var hashes []common.Hash
for i := 0; i < params.MaxBlobsPerTx; i++ {
for i := 0; i < params.MaxBlobsPerBlock; i++ {
var blobElements types.Blob
blob := randomBlob()
for j := range blob {
Expand Down Expand Up @@ -211,7 +211,7 @@ func BenchmarkBatchVerifyWithoutKZGProofs(b *testing.B) {
for i := 0; i < siz; i++ {
var blobs [][]bls.Fr
var commitments []*bls.G1Point
for i := 0; i < params.MaxBlobsPerTx; i++ {
for i := 0; i < params.MaxBlobsPerBlock; i++ {
blob := randomBlob()
blobs = append(blobs, blob)
commitments = append(commitments, kzg.BlobToKzg(blob))
Expand Down

0 comments on commit 28e8789

Please sign in to comment.