From ac3f26947dcc00d39afcabaa9171a09b899aa7d9 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:41:31 +0100 Subject: [PATCH] implement eip 2935 (#342) * implement eip 2935 * add touched historical contract slot to the witness --- cmd/evm/internal/t8ntool/execution.go | 4 ++++ cmd/evm/internal/t8ntool/gen_stenv.go | 6 ++++++ core/state_processor.go | 9 +++++++++ core/vm/instructions.go | 29 +++++++++++++++++++++++++++ miner/worker.go | 3 +++ params/protocol_params.go | 9 ++++++++- 6 files changed, 59 insertions(+), 1 deletion(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index c408623fe32f..8c6725cb316b 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -88,6 +88,7 @@ type stEnv struct { ExcessBlobGas *uint64 `json:"excessBlobGas,omitempty"` ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"` + ParentHash *common.Hash `json:"parentHash,omitempty"` } type stEnvMarshaling struct { @@ -182,6 +183,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { misc.ApplyDAOHardFork(statedb) } + if chainConfig.IsPrague(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) { + core.ProcessParentBlockHash(statedb, pre.Env.Number-1, *pre.Env.ParentHash) + } var blobGasUsed uint64 for i, tx := range txs { if tx.Type() == types.BlobTxType && vmContext.ExcessBlobGas == nil { diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go index f50fee5e31c3..5436407f07bb 100644 --- a/cmd/evm/internal/t8ntool/gen_stenv.go +++ b/cmd/evm/internal/t8ntool/gen_stenv.go @@ -36,6 +36,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"` ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` + ParentHash *common.Hash `json:"parentHash,omitempty"` } var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) @@ -57,6 +58,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) { enc.ExcessBlobGas = (*math.HexOrDecimal64)(s.ExcessBlobGas) enc.ParentExcessBlobGas = (*math.HexOrDecimal64)(s.ParentExcessBlobGas) enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed) + enc.ParentHash = s.ParentHash return json.Marshal(&enc) } @@ -82,6 +84,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas,omitempty"` ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` + ParentHash *common.Hash `json:"parentHash,omitempty"` } var dec stEnv if err := json.Unmarshal(input, &dec); err != nil { @@ -148,5 +151,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { if dec.ParentBlobGasUsed != nil { s.ParentBlobGasUsed = (*uint64)(dec.ParentBlobGasUsed) } + if dec.ParentHash != nil { + s.ParentHash = dec.ParentHash + } return nil } diff --git a/core/state_processor.go b/core/state_processor.go index 84c2f6429a30..ed55c478843c 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -85,6 +85,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) signer = types.MakeSigner(p.config, header.Number, header.Time) ) + if p.config.IsPrague(block.Number(), block.Time()) { + ProcessParentBlockHash(statedb, block.NumberU64()-1, block.ParentHash()) + } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := TransactionToMessage(tx, signer, header.BaseFee) @@ -359,3 +362,9 @@ func (kvm *keyValueMigrator) migrateCollectedKeyValues(tree *trie.VerkleTrie) er return nil } + +func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash) { + var key common.Hash + binary.BigEndian.PutUint64(key[24:], prevNumber) + statedb.SetState(params.HistoryStorageAddress, key, prevHash) +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index a7860f0fde82..00787b3d030f 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -17,6 +17,8 @@ package vm import ( + "encoding/binary" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/state" @@ -497,6 +499,13 @@ func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ return nil, nil } +func getBlockHashFromContract(number uint64, statedb StateDB, witness *state.AccessWitness) common.Hash { + var pnum common.Hash + binary.BigEndian.PutUint64(pnum[24:], number) + witness.TouchAddressOnReadAndComputeGas(params.HistoryStorageAddress[:], *uint256.NewInt(number / 256), byte(number&0xFF)) + return statedb.GetState(params.HistoryStorageAddress, pnum) +} + func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { num := scope.Stack.peek() num64, overflow := num.Uint64WithOverflow() @@ -504,6 +513,26 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( num.Clear() return nil, nil } + + evm := interpreter.evm + bnum := evm.Context.BlockNumber.Uint64() + // if Prague is active, check if we are past the 256th block so that + // reading from the contract can be activated (EIP 2935). + if evm.chainRules.IsPrague && bnum > 256 { + if getBlockHashFromContract(bnum-256, evm.StateDB, evm.Accesses) != (common.Hash{}) { + // EIP-2935 case: get the block number from the fork, as we are 256 blocks + // after the fork activation. + + num.SetBytes(getBlockHashFromContract(num64, evm.StateDB, evm.Accesses).Bytes()) + return nil, nil + } + + // if the 256th ancestor didn't have its hash stored in the + // history contract, then we are within 256 blocks of the + // fork activation, and the former behavior should be retained. + // Fall through the legacy use case. + } + var upper, lower uint64 upper = interpreter.evm.Context.BlockNumber.Uint64() if upper < 257 { diff --git a/miner/worker.go b/miner/worker.go index 124c93212262..ef8d6087b7cd 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -920,6 +920,9 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { log.Error("Failed to create sealing context", "err", err) return nil, err } + if w.chainConfig.IsPrague(header.Number, header.Time) { + core.ProcessParentBlockHash(env.state, header.Number.Uint64()-1, header.ParentHash) + } return env, nil } diff --git a/params/protocol_params.go b/params/protocol_params.go index a407ed147329..6d91ee48a85f 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -16,7 +16,11 @@ package params -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) const ( GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations. @@ -179,4 +183,7 @@ var ( GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. + + // HistoryStorageAddress is where the historical block hashes are stored. + HistoryStorageAddress common.Address = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") )