core: implement BLOBBASEFEE opcode (0x4a) (#28098)

Implements "EIP-7516: BLOBBASEFEE opcode" for cancun, as per spec: https://eips.ethereum.org/EIPS/eip-7516
This commit is contained in:
Marius van der Wijden 2023-10-02 11:49:29 +02:00 committed by GitHub
parent 7b6ff527d5
commit c39cbc1a78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 78 additions and 42 deletions

View File

@ -163,17 +163,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
rnd := common.BigToHash(pre.Env.Random) rnd := common.BigToHash(pre.Env.Random)
vmContext.Random = &rnd vmContext.Random = &rnd
} }
// If excessBlobGas is defined, add it to the vmContext. // Calculate the BlobBaseFee
var excessBlobGas uint64
if pre.Env.ExcessBlobGas != nil { if pre.Env.ExcessBlobGas != nil {
vmContext.ExcessBlobGas = pre.Env.ExcessBlobGas excessBlobGas := *pre.Env.ExcessBlobGas
vmContext.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
} else { } else {
// If it is not explicitly defined, but we have the parent values, we try // If it is not explicitly defined, but we have the parent values, we try
// to calculate it ourselves. // to calculate it ourselves.
parentExcessBlobGas := pre.Env.ParentExcessBlobGas parentExcessBlobGas := pre.Env.ParentExcessBlobGas
parentBlobGasUsed := pre.Env.ParentBlobGasUsed parentBlobGasUsed := pre.Env.ParentBlobGasUsed
if parentExcessBlobGas != nil && parentBlobGasUsed != nil { if parentExcessBlobGas != nil && parentBlobGasUsed != nil {
excessBlobGas := eip4844.CalcExcessBlobGas(*parentExcessBlobGas, *parentBlobGasUsed) excessBlobGas = eip4844.CalcExcessBlobGas(*parentExcessBlobGas, *parentBlobGasUsed)
vmContext.ExcessBlobGas = &excessBlobGas vmContext.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
} }
} }
// If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's // If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's
@ -189,7 +191,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
} }
var blobGasUsed uint64 var blobGasUsed uint64
for i, tx := range txs { for i, tx := range txs {
if tx.Type() == types.BlobTxType && vmContext.ExcessBlobGas == nil { if tx.Type() == types.BlobTxType && vmContext.BlobBaseFee == nil {
errMsg := "blob tx used but field env.ExcessBlobGas missing" errMsg := "blob tx used but field env.ExcessBlobGas missing"
log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", errMsg) log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", errMsg)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg}) rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg})
@ -322,8 +324,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil))
execRs.WithdrawalsRoot = &h execRs.WithdrawalsRoot = &h
} }
if vmContext.ExcessBlobGas != nil { if vmContext.BlobBaseFee != nil {
execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(vmContext.ExcessBlobGas) execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(&excessBlobGas)
execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed) execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed)
} }
// Re-create statedb instance with new root upon the updated database // Re-create statedb instance with new root upon the updated database

View File

@ -123,7 +123,8 @@ func runCmd(ctx *cli.Context) error {
sender = common.BytesToAddress([]byte("sender")) sender = common.BytesToAddress([]byte("sender"))
receiver = common.BytesToAddress([]byte("receiver")) receiver = common.BytesToAddress([]byte("receiver"))
preimages = ctx.Bool(DumpFlag.Name) preimages = ctx.Bool(DumpFlag.Name)
blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests
blobBaseFee = new(big.Int) // TODO (MariusVanDerWijden) implement blob fee in state tests
) )
if ctx.Bool(MachineFlag.Name) { if ctx.Bool(MachineFlag.Name) {
tracer = logger.NewJSONLogger(logconfig, os.Stdout) tracer = logger.NewJSONLogger(logconfig, os.Stdout)
@ -221,6 +222,7 @@ func runCmd(ctx *cli.Context) error {
Coinbase: genesisConfig.Coinbase, Coinbase: genesisConfig.Coinbase,
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number), BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
BlobHashes: blobHashes, BlobHashes: blobHashes,
BlobBaseFee: blobBaseFee,
EVMConfig: vm.Config{ EVMConfig: vm.Config{
Tracer: tracer, Tracer: tracer,
}, },

View File

@ -21,6 +21,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
) )
@ -40,6 +41,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
var ( var (
beneficiary common.Address beneficiary common.Address
baseFee *big.Int baseFee *big.Int
blobBaseFee *big.Int
random *common.Hash random *common.Hash
) )
@ -52,21 +54,24 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
if header.BaseFee != nil { if header.BaseFee != nil {
baseFee = new(big.Int).Set(header.BaseFee) baseFee = new(big.Int).Set(header.BaseFee)
} }
if header.ExcessBlobGas != nil {
blobBaseFee = eip4844.CalcBlobFee(*header.ExcessBlobGas)
}
if header.Difficulty.Cmp(common.Big0) == 0 { if header.Difficulty.Cmp(common.Big0) == 0 {
random = &header.MixDigest random = &header.MixDigest
} }
return vm.BlockContext{ return vm.BlockContext{
CanTransfer: CanTransfer, CanTransfer: CanTransfer,
Transfer: Transfer, Transfer: Transfer,
GetHash: GetHashFn(header, chain), GetHash: GetHashFn(header, chain),
Coinbase: beneficiary, Coinbase: beneficiary,
BlockNumber: new(big.Int).Set(header.Number), BlockNumber: new(big.Int).Set(header.Number),
Time: header.Time, Time: header.Time,
Difficulty: new(big.Int).Set(header.Difficulty), Difficulty: new(big.Int).Set(header.Difficulty),
BaseFee: baseFee, BaseFee: baseFee,
GasLimit: header.GasLimit, BlobBaseFee: blobBaseFee,
Random: random, GasLimit: header.GasLimit,
ExcessBlobGas: header.ExcessBlobGas, Random: random,
} }
} }

View File

@ -24,7 +24,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -138,7 +137,7 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
if tx.Type() == types.BlobTxType { if tx.Type() == types.BlobTxType {
receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob) receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob)
receipt.BlobGasPrice = eip4844.CalcBlobFee(*evm.Context.ExcessBlobGas) receipt.BlobGasPrice = evm.Context.BlobBaseFee
} }
// If the transaction created a contract, store the creation address in the receipt. // If the transaction created a contract, store the creation address in the receipt.

View File

@ -24,7 +24,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
cmath "github.com/ethereum/go-ethereum/common/math" cmath "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -248,7 +247,7 @@ func (st *StateTransition) buyGas() error {
balanceCheck.Add(balanceCheck, blobBalanceCheck) balanceCheck.Add(balanceCheck, blobBalanceCheck)
// Pay for blobGasUsed * actual blob fee // Pay for blobGasUsed * actual blob fee
blobFee := new(big.Int).SetUint64(blobGas) blobFee := new(big.Int).SetUint64(blobGas)
blobFee.Mul(blobFee, eip4844.CalcBlobFee(*st.evm.Context.ExcessBlobGas)) blobFee.Mul(blobFee, st.evm.Context.BlobBaseFee)
mgval.Add(mgval, blobFee) mgval.Add(mgval, blobFee)
} }
} }
@ -329,7 +328,7 @@ func (st *StateTransition) preCheck() error {
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) { if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
if st.blobGasUsed() > 0 { if st.blobGasUsed() > 0 {
// Check that the user is paying at least the current blob fee // Check that the user is paying at least the current blob fee
blobFee := eip4844.CalcBlobFee(*st.evm.Context.ExcessBlobGas) blobFee := st.evm.Context.BlobBaseFee
if st.msg.BlobGasFeeCap.Cmp(blobFee) < 0 { if st.msg.BlobGasFeeCap.Cmp(blobFee) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrBlobFeeCapTooLow, st.msg.From.Hex(), st.msg.BlobGasFeeCap, blobFee) return fmt.Errorf("%w: address %v have %v want %v", ErrBlobFeeCapTooLow, st.msg.From.Hex(), st.msg.BlobGasFeeCap, blobFee)
} }

View File

@ -282,9 +282,15 @@ func opBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
return nil, nil return nil, nil
} }
// enable4844 applies EIP-4844 (DATAHASH opcode) // opBlobBaseFee implements BLOBBASEFEE opcode
func opBlobBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
blobBaseFee, _ := uint256.FromBig(interpreter.evm.Context.BlobBaseFee)
scope.Stack.push(blobBaseFee)
return nil, nil
}
// enable4844 applies EIP-4844 (BLOBHASH opcode)
func enable4844(jt *JumpTable) { func enable4844(jt *JumpTable) {
// New opcode
jt[BLOBHASH] = &operation{ jt[BLOBHASH] = &operation{
execute: opBlobHash, execute: opBlobHash,
constantGas: GasFastestStep, constantGas: GasFastestStep,
@ -293,6 +299,16 @@ func enable4844(jt *JumpTable) {
} }
} }
// enable7516 applies EIP-7516 (BLOBBASEFEE opcode)
func enable7516(jt *JumpTable) {
jt[BLOBBASEFEE] = &operation{
execute: opBlobBaseFee,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
}
// enable6780 applies EIP-6780 (deactivate SELFDESTRUCT) // enable6780 applies EIP-6780 (deactivate SELFDESTRUCT)
func enable6780(jt *JumpTable) { func enable6780(jt *JumpTable) {
jt[SELFDESTRUCT] = &operation{ jt[SELFDESTRUCT] = &operation{

View File

@ -67,14 +67,14 @@ type BlockContext struct {
GetHash GetHashFunc GetHash GetHashFunc
// Block information // Block information
Coinbase common.Address // Provides information for COINBASE Coinbase common.Address // Provides information for COINBASE
GasLimit uint64 // Provides information for GASLIMIT GasLimit uint64 // Provides information for GASLIMIT
BlockNumber *big.Int // Provides information for NUMBER BlockNumber *big.Int // Provides information for NUMBER
Time uint64 // Provides information for TIME Time uint64 // Provides information for TIME
Difficulty *big.Int // Provides information for DIFFICULTY Difficulty *big.Int // Provides information for DIFFICULTY
BaseFee *big.Int // Provides information for BASEFEE BaseFee *big.Int // Provides information for BASEFEE
Random *common.Hash // Provides information for PREVRANDAO BlobBaseFee *big.Int // Provides information for BLOBBASEFEE
ExcessBlobGas *uint64 // ExcessBlobGas field in the header, needed to compute the data Random *common.Hash // Provides information for PREVRANDAO
} }
// TxContext provides the EVM with information about a transaction. // TxContext provides the EVM with information about a transaction.

View File

@ -82,7 +82,8 @@ func validate(jt JumpTable) JumpTable {
func newCancunInstructionSet() JumpTable { func newCancunInstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet() instructionSet := newShanghaiInstructionSet()
enable4844(&instructionSet) // EIP-4844 (DATAHASH opcode) enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
enable7516(&instructionSet) // EIP-7516 (BLOBBASEFEE opcode)
enable1153(&instructionSet) // EIP-1153 "Transient Storage" enable1153(&instructionSet) // EIP-1153 "Transient Storage"
enable5656(&instructionSet) // EIP-5656 (MCOPY opcode) enable5656(&instructionSet) // EIP-5656 (MCOPY opcode)
enable6780(&instructionSet) // EIP-6780 SELFDESTRUCT only in same transaction enable6780(&instructionSet) // EIP-6780 SELFDESTRUCT only in same transaction

View File

@ -101,6 +101,7 @@ const (
SELFBALANCE OpCode = 0x47 SELFBALANCE OpCode = 0x47
BASEFEE OpCode = 0x48 BASEFEE OpCode = 0x48
BLOBHASH OpCode = 0x49 BLOBHASH OpCode = 0x49
BLOBBASEFEE OpCode = 0x4a
) )
// 0x50 range - 'storage' and execution. // 0x50 range - 'storage' and execution.
@ -287,6 +288,7 @@ var opCodeToString = map[OpCode]string{
SELFBALANCE: "SELFBALANCE", SELFBALANCE: "SELFBALANCE",
BASEFEE: "BASEFEE", BASEFEE: "BASEFEE",
BLOBHASH: "BLOBHASH", BLOBHASH: "BLOBHASH",
BLOBBASEFEE: "BLOBBASEFEE",
// 0x50 range - 'storage' and execution. // 0x50 range - 'storage' and execution.
POP: "POP", POP: "POP",
@ -444,6 +446,7 @@ var stringToOp = map[string]OpCode{
"CHAINID": CHAINID, "CHAINID": CHAINID,
"BASEFEE": BASEFEE, "BASEFEE": BASEFEE,
"BLOBHASH": BLOBHASH, "BLOBHASH": BLOBHASH,
"BLOBBASEFEE": BLOBBASEFEE,
"DELEGATECALL": DELEGATECALL, "DELEGATECALL": DELEGATECALL,
"STATICCALL": STATICCALL, "STATICCALL": STATICCALL,
"CODESIZE": CODESIZE, "CODESIZE": CODESIZE,

View File

@ -37,6 +37,7 @@ func NewEnv(cfg *Config) *vm.EVM {
Difficulty: cfg.Difficulty, Difficulty: cfg.Difficulty,
GasLimit: cfg.GasLimit, GasLimit: cfg.GasLimit,
BaseFee: cfg.BaseFee, BaseFee: cfg.BaseFee,
BlobBaseFee: cfg.BlobBaseFee,
Random: cfg.Random, Random: cfg.Random,
} }

View File

@ -44,6 +44,7 @@ type Config struct {
Debug bool Debug bool
EVMConfig vm.Config EVMConfig vm.Config
BaseFee *big.Int BaseFee *big.Int
BlobBaseFee *big.Int
BlobHashes []common.Hash BlobHashes []common.Hash
Random *common.Hash Random *common.Hash
@ -95,6 +96,9 @@ func setDefaults(cfg *Config) {
if cfg.BaseFee == nil { if cfg.BaseFee == nil {
cfg.BaseFee = big.NewInt(params.InitialBaseFee) cfg.BaseFee = big.NewInt(params.InitialBaseFee)
} }
if cfg.BlobBaseFee == nil {
cfg.BlobBaseFee = new(big.Int)
}
} }
// Execute executes the code using the input as call data during the execution. // Execute executes the code using the input as call data during the execution.

View File

@ -991,13 +991,14 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
// BlockOverrides is a set of header fields to override. // BlockOverrides is a set of header fields to override.
type BlockOverrides struct { type BlockOverrides struct {
Number *hexutil.Big Number *hexutil.Big
Difficulty *hexutil.Big Difficulty *hexutil.Big
Time *hexutil.Uint64 Time *hexutil.Uint64
GasLimit *hexutil.Uint64 GasLimit *hexutil.Uint64
Coinbase *common.Address Coinbase *common.Address
Random *common.Hash Random *common.Hash
BaseFee *hexutil.Big BaseFee *hexutil.Big
BlobBaseFee *hexutil.Big
} }
// Apply overrides the given header fields into the given block context. // Apply overrides the given header fields into the given block context.
@ -1026,6 +1027,9 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
if diff.BaseFee != nil { if diff.BaseFee != nil {
blockCtx.BaseFee = diff.BaseFee.ToInt() blockCtx.BaseFee = diff.BaseFee.ToInt()
} }
if diff.BlobBaseFee != nil {
blockCtx.BlobBaseFee = diff.BlobBaseFee.ToInt()
}
} }
// ChainContextBackend provides methods required to implement ChainContext. // ChainContextBackend provides methods required to implement ChainContext.