From da8c9379e0dabacc3e83c7e516e42e810fbf9426 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")
 )