core: implement eip-2935

Co-authored-by: Guillaume Ballet <gballet@gmail.com>
Co-authored-by: Ignacio Hagopian <jsign.uy@gmail.com>
This commit is contained in:
Sina Mahmoodi 2024-04-04 06:30:52 +02:00 committed by lightclient
parent 44a50c9f96
commit 20f1d2ceed
No known key found for this signature in database
GPG key ID: 75C916AFEE20183E
8 changed files with 138 additions and 4 deletions

View file

@ -196,6 +196,16 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig) evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb) core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
} }
if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) {
// insert all parent hashes in the contract
for i, h := range pre.Env.BlockHashes {
n := uint64(i)
if n >= pre.Env.Number || pre.Env.Number > (n+params.HistoryServeWindow) {
continue
}
core.ProcessParentBlockHash(statedb, n, h)
}
}
for i := 0; txIt.Next(); i++ { for i := 0; txIt.Next(); i++ {
tx, err := txIt.Tx() tx, err := txIt.Tx()

View file

@ -593,6 +593,8 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
// Pre-deploy EIP-2935 history contract.
params.HistoryStorageAddress: types.Account{Nonce: 1, Code: params.HistoryStorageCode},
}, },
} }
if faucet != nil { if faucet != nil {

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
@ -80,6 +81,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if beaconRoot := block.BeaconRoot(); beaconRoot != nil { if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
} }
if p.config.IsPrague(block.Number(), block.Time()) {
ProcessBlockHashHistory(statedb, block.Header(), p.config, p.bc)
}
// Iterate over and process the individual transactions // Iterate over and process the individual transactions
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
msg, err := TransactionToMessage(tx, signer, header.BaseFee) msg, err := TransactionToMessage(tx, signer, header.BaseFee)
@ -209,3 +213,37 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) _, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
statedb.Finalise(true) statedb.Finalise(true)
} }
// ProcessBlockHashHistory is called at every block to insert the parent block hash
// in the history storage contract as per EIP-2935. At the EIP-2935 fork block, it
// populates the whole buffer with block hashes.
func ProcessBlockHashHistory(statedb *state.StateDB, header *types.Header, chainConfig *params.ChainConfig, chain consensus.ChainHeaderReader) {
var (
prevHash = header.ParentHash
parent = chain.GetHeaderByHash(prevHash)
number = header.Number.Uint64()
prevNumber = parent.Number.Uint64()
)
ProcessParentBlockHash(statedb, prevNumber, prevHash)
// History already inserted.
if chainConfig.IsPrague(parent.Number, parent.Time) || prevNumber == 0 {
return
}
var low uint64
if number > params.HistoryServeWindow {
low = number - params.HistoryServeWindow
}
for i := prevNumber; i > low; i-- {
ProcessParentBlockHash(statedb, i-1, parent.ParentHash)
parent = chain.GetHeader(parent.ParentHash, i-1)
}
}
// ProcessParentBlockHash stores the parent block hash in the history storage contract
// as per EIP-2935.
func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash) {
ringIndex := prevNumber % params.HistoryServeWindow
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)
statedb.SetState(params.HistoryStorageAddress, key, prevHash)
}

View file

@ -18,6 +18,7 @@ package core
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"encoding/binary"
"math/big" "math/big"
"testing" "testing"
@ -29,9 +30,11 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"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"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256" "github.com/holiman/uint256"
@ -528,3 +531,55 @@ func TestProcessVerkle(t *testing.T) {
} }
} }
} }
type MockChain struct {
chain map[common.Hash]*types.Header
}
func (m *MockChain) Config() *params.ChainConfig { return nil }
func (m *MockChain) CurrentHeader() *types.Header { return nil }
func (m *MockChain) GetHeaderByNumber(number uint64) *types.Header { return nil }
func (m *MockChain) GetTd(hash common.Hash, number uint64) *big.Int { return nil }
func (m *MockChain) GetHeaderByHash(hash common.Hash) *types.Header {
return m.chain[hash]
}
func (m *MockChain) GetHeader(hash common.Hash, number uint64) *types.Header {
return m.chain[hash]
}
func TestProcessBlockHashHistory(t *testing.T) {
hashA := common.Hash{0x01}
hashB := common.Hash{0x02}
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
header := &types.Header{ParentHash: hashA, Number: big.NewInt(2)}
parent := &types.Header{ParentHash: hashB, Number: big.NewInt(1)}
parentParent := &types.Header{ParentHash: common.Hash{}, Number: big.NewInt(0)}
chainConfig := params.AllDevChainProtocolChanges
chainConfig.PragueTime = nil
chain := new(MockChain)
chain.chain = make(map[common.Hash]*types.Header)
chain.chain[hashA] = parent
chain.chain[hashB] = parentParent
ProcessBlockHashHistory(statedb, header, chainConfig, chain)
// make sure that the state is correct
if have := getParentBlockHash(statedb, 1); have != hashA {
t.Fail()
}
if have := getParentBlockHash(statedb, 0); have != hashB {
t.Fail()
}
}
func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash {
ringIndex := number % params.HistoryServeWindow
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)
return statedb.GetState(params.HistoryStorageAddress, key)
}

View file

@ -17,6 +17,7 @@
package vm package vm
import ( import (
"encoding/binary"
"math" "math"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -434,16 +435,27 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
num.Clear() num.Clear()
return nil, nil return nil, nil
} }
historySize := uint64(256)
isPrague := interpreter.evm.ChainConfig().IsPrague(interpreter.evm.Context.BlockNumber, interpreter.evm.Context.Time)
// EIP-2935 extends the observable history window.
if isPrague {
historySize = params.HistoryServeWindow
}
var upper, lower uint64 var upper, lower uint64
upper = interpreter.evm.Context.BlockNumber.Uint64() upper = interpreter.evm.Context.BlockNumber.Uint64()
if upper < 257 { if upper < historySize+1 {
lower = 0 lower = 0
} else { } else {
lower = upper - 256 lower = upper - historySize
} }
if num64 >= lower && num64 < upper { if num64 >= lower && num64 < upper {
if isPrague {
var key common.Hash
binary.BigEndian.PutUint64(key[24:], num64 % params.HistoryServeWindow)
num.SetBytes(interpreter.evm.StateDB.GetState(params.HistoryStorageAddress, key).Bytes())
} else {
num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes())
}
} else { } else {
num.Clear() num.Clear()
} }

View file

@ -87,6 +87,11 @@ func newVerkleInstructionSet() JumpTable {
return validate(instructionSet) return validate(instructionSet)
} }
func newPraugeInstructionSet() JumpTable {
instructionSet := newCancunInstructionSet()
return validate(instructionSet)
}
func newCancunInstructionSet() JumpTable { func newCancunInstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet() instructionSet := newShanghaiInstructionSet()
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode) enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)

View file

@ -200,6 +200,9 @@ func (miner *Miner) prepareWork(genParams *generateParams) (*environment, error)
vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, miner.chainConfig, vm.Config{}) vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, miner.chainConfig, vm.Config{})
core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state) core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state)
} }
if miner.chainConfig.IsPrague(header.Number, header.Time) {
core.ProcessBlockHashHistory(env.state, header, miner.chainConfig, miner.chain)
}
return env, nil return env, nil
} }

View file

@ -174,6 +174,8 @@ const (
BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing) BlobTxTargetBlobGasPerBlock = 3 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing)
MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block
HistoryServeWindow = 8192 // Number of blocks to serve historical block hashes for, EIP-2935.
) )
// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations // Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations
@ -189,4 +191,11 @@ var (
BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02") BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02")
// SystemAddress is where the system-transaction is sent from as per EIP-4788 // SystemAddress is where the system-transaction is sent from as per EIP-4788
SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
// HistoryStorageAddress is where the historical block hashes are stored.
HistoryStorageAddress = common.HexToAddress("0x25a219378dad9b3503c8268c9ca836a52427a4fb")
// HistoryStorageCode is the code with getters for historical block hashes.
HistoryStorageCode = common.FromHex("60203611603157600143035f35116029575f356120000143116029576120005f3506545f5260205ff35b5f5f5260205ff35b5f5ffd00")
// Magic prefix for EIP-3074 AUTH messages.
AuthMagic = byte(0x04)
) )