EIP-4788 v2 (no precompile) (#8038)

See https://github.com/ethereum/EIPs/pull/7456 &
https://github.com/ethereum/go-ethereum/pull/27849. Also set the gas
limit for system calls to 30M (previously 2^64-1), which is in line with
the [Gnosis
spec](https://github.com/gnosischain/specs/blob/master/execution/withdrawals.md#specification),
but should be doubled checked for Gnosis Chain.
This commit is contained in:
Andrew Ashikhmin 2023-08-24 17:10:50 +02:00 committed by GitHub
parent bb2c2adbb6
commit 1fd9d20e14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 40 additions and 195 deletions

View File

@ -66,7 +66,6 @@ var (
// diffNoTurn = big.NewInt(1) // Block difficulty for out-of-turn signatures
validatorHeaderBytesLength = length.Addr + 20 // address + power
// systemAddress = libcommon.HexToAddress("0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE")
)
// Various error messages to mark blocks invalid. These should be private to

View File

@ -272,7 +272,9 @@ func (s *Merge) Initialize(config *chain.Config, chain consensus.ChainHeaderRead
s.eth1Engine.Initialize(config, chain, header, state, syscall)
}
if chain.Config().IsCancun(header.Time) {
misc.ApplyBeaconRootEip4788(chain, header, state)
misc.ApplyBeaconRootEip4788(header.ParentBeaconBlockRoot, func(addr libcommon.Address, data []byte) ([]byte, error) {
return syscall(addr, data, state, header, false /* constCall */)
})
}
}

View File

@ -1,23 +1,16 @@
package misc
import (
"github.com/holiman/uint256"
"github.com/ledgerwatch/log/v3"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/consensus"
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/params"
)
func ApplyBeaconRootEip4788(chain consensus.ChainHeaderReader, header *types.Header, state *state.IntraBlockState) {
historyStorageAddress := libcommon.BytesToAddress(params.HistoryStorageAddress)
historicalRootsModulus := params.HistoricalRootsModulus
timestampReduced := header.Time % historicalRootsModulus
timestampExtended := timestampReduced + historicalRootsModulus
timestampIndex := libcommon.BytesToHash((uint256.NewInt(timestampReduced)).Bytes())
rootIndex := libcommon.BytesToHash(uint256.NewInt(timestampExtended).Bytes())
parentBeaconBlockRootInt := *uint256.NewInt(0).SetBytes(header.ParentBeaconBlockRoot.Bytes())
state.SetState(historyStorageAddress, &timestampIndex, *uint256.NewInt(header.Time))
state.SetState(historyStorageAddress, &rootIndex, parentBeaconBlockRootInt)
func ApplyBeaconRootEip4788(parentBeaconBlockRoot *libcommon.Hash, syscall consensus.SystemCall) {
_, err := syscall(params.BeaconRootsAddress, parentBeaconBlockRoot.Bytes())
if err != nil {
log.Warn("Failed to call beacon roots contract", "err", err)
}
}

View File

@ -1,94 +0,0 @@
package misc
import (
"crypto/sha256"
"math/big"
"testing"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/params"
"github.com/stretchr/testify/assert"
)
type dummyChainHeaderReader struct {
}
func (r dummyChainHeaderReader) Config() *chain.Config {
return nil
}
func (r dummyChainHeaderReader) CurrentHeader() *types.Header {
return nil
}
func (r dummyChainHeaderReader) GetHeader(libcommon.Hash, uint64) *types.Header {
return nil
}
func (r dummyChainHeaderReader) GetHeaderByNumber(uint64) *types.Header {
return nil
}
func (r dummyChainHeaderReader) GetHeaderByHash(libcommon.Hash) *types.Header {
return nil
}
func (r dummyChainHeaderReader) GetTd(libcommon.Hash, uint64) *big.Int {
return nil
}
func (r dummyChainHeaderReader) FrozenBlocks() uint64 {
return 0
}
type dummyStateReader struct {
}
func (dsr *dummyStateReader) ReadAccountData(address libcommon.Address) (*accounts.Account, error) {
return nil, nil
}
func (dsr *dummyStateReader) ReadAccountStorage(address libcommon.Address, incarnation uint64, key *libcommon.Hash) ([]byte, error) {
return nil, nil
}
func (dsr *dummyStateReader) ReadAccountCode(address libcommon.Address, incarnation uint64, codeHash libcommon.Hash) ([]byte, error) {
return make([]byte, 0), nil
}
func (dsr *dummyStateReader) ReadAccountCodeSize(address libcommon.Address, incarnation uint64, codeHash libcommon.Hash) (int, error) {
return 0, nil
}
func (dsr *dummyStateReader) ReadAccountIncarnation(address libcommon.Address) (uint64, error) {
return 0, nil
}
func TestApplyBeaconRoot(t *testing.T) {
var mockReader dummyChainHeaderReader
testHashBytes := sha256.Sum256([]byte("test"))
testRootHash := libcommon.BytesToHash(testHashBytes[:])
header := types.Header{
ParentBeaconBlockRoot: &testRootHash,
Time: 1,
}
var state state.IntraBlockState = *state.New(&dummyStateReader{})
ApplyBeaconRootEip4788(mockReader, &header, &state)
pc := vm.PrecompiledContractsCancun[libcommon.BytesToAddress(params.HistoryStorageAddress)]
spc, ok := pc.(vm.StatefulPrecompiledContract)
if !ok {
t.Fatalf("Error instantiating pre-compile")
}
timestampParam := uint256.NewInt(1).Bytes32()
res, err := spc.RunStateful(timestampParam[:], &state)
if err != nil {
t.Errorf("error %v", err)
}
assert.Equal(t, testRootHash.Bytes(), res, "Beacon root mismatch")
t.Logf("result %v", res)
}

View File

@ -49,6 +49,9 @@ type SyncMode string
const (
TriesInMemory = 128
// See gas_limit in https://github.com/gnosischain/specs/blob/master/execution/withdrawals.md
SysCallGasLimit = uint64(30_000_000)
)
type RejectedTx struct {
@ -216,7 +219,8 @@ func SysCallContract(contract libcommon.Address, data []byte, chainConfig *chain
state.SystemAddress,
&contract,
0, u256.Num0,
math.MaxUint64, u256.Num0,
SysCallGasLimit,
u256.Num0,
nil, nil,
data, nil, false,
true, // isFree
@ -257,7 +261,8 @@ func SysCreate(contract libcommon.Address, data []byte, chainConfig chain.Config
contract,
nil, // to
0, u256.Num0,
math.MaxUint64, u256.Num0,
SysCallGasLimit,
u256.Num0,
nil, nil,
data, nil, false,
true, // isFree

View File

@ -17,7 +17,6 @@
package vm
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"errors"
@ -31,7 +30,6 @@ import (
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/crypto/blake2b"
"github.com/ledgerwatch/erigon/crypto/bls12381"
@ -51,12 +49,6 @@ type PrecompiledContract interface {
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
}
type StatefulPrecompiledContract interface {
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
RunStateful(input []byte, state evmtypes.IntraBlockState) ([]byte, error) // Run runs the precompiled contract
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
}
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
// contracts used in the Frontier and Homestead releases.
var PrecompiledContractsHomestead = map[libcommon.Address]PrecompiledContract{
@ -108,21 +100,16 @@ var PrecompiledContractsBerlin = map[libcommon.Address]PrecompiledContract{
}
var PrecompiledContractsCancun = map[libcommon.Address]PrecompiledContract{
libcommon.BytesToAddress([]byte{1}): &ecrecover{},
libcommon.BytesToAddress([]byte{2}): &sha256hash{},
libcommon.BytesToAddress([]byte{3}): &ripemd160hash{},
libcommon.BytesToAddress([]byte{4}): &dataCopy{},
libcommon.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
libcommon.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
libcommon.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
libcommon.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
libcommon.BytesToAddress([]byte{9}): &blake2F{},
libcommon.BytesToAddress([]byte{0x0a}): &pointEvaluation{},
libcommon.BytesToAddress(params.HistoryStorageAddress): &parentBeaconBlockRoot{},
}
var StatefulPrecompile = map[libcommon.Address]bool{
libcommon.BytesToAddress(params.HistoryStorageAddress): true,
libcommon.BytesToAddress([]byte{0x01}): &ecrecover{},
libcommon.BytesToAddress([]byte{0x02}): &sha256hash{},
libcommon.BytesToAddress([]byte{0x03}): &ripemd160hash{},
libcommon.BytesToAddress([]byte{0x04}): &dataCopy{},
libcommon.BytesToAddress([]byte{0x05}): &bigModExp{eip2565: true},
libcommon.BytesToAddress([]byte{0x06}): &bn256AddIstanbul{},
libcommon.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{},
libcommon.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{},
libcommon.BytesToAddress([]byte{0x09}): &blake2F{},
libcommon.BytesToAddress([]byte{0x0a}): &pointEvaluation{},
}
// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
@ -186,19 +173,14 @@ func ActivePrecompiles(rules *chain.Rules) []libcommon.Address {
// - the returned bytes,
// - the _remaining_ gas,
// - any error that occurred
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, state evmtypes.IntraBlockState) (ret []byte, remainingGas uint64, err error) {
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64,
) (ret []byte, remainingGas uint64, err error) {
gasCost := p.RequiredGas(input)
if suppliedGas < gasCost {
return nil, 0, ErrOutOfGas
}
suppliedGas -= gasCost
var output []byte
sp, isStateful := p.(StatefulPrecompiledContract)
if isStateful {
output, err = sp.RunStateful(input, state)
} else {
output, err = p.Run(input)
}
output, err := p.Run(input)
return output, suppliedGas, err
}
@ -1116,36 +1098,3 @@ func (c *pointEvaluation) RequiredGas(input []byte) uint64 {
func (c *pointEvaluation) Run(input []byte) ([]byte, error) {
return libkzg.PointEvaluationPrecompile(input)
}
type parentBeaconBlockRoot struct{}
func (c *parentBeaconBlockRoot) RequiredGas(input []byte) uint64 {
return params.ParentBeaconBlockRootGas
}
func (c *parentBeaconBlockRoot) Run(input []byte) ([]byte, error) {
return nil, nil
}
func (c *parentBeaconBlockRoot) RunStateful(input []byte, state evmtypes.IntraBlockState) ([]byte, error) {
timestampParam := input[:32]
if len(timestampParam) < 32 {
return nil, errors.New("timestamp param too short")
}
timestampReduced := uint256.NewInt(0).SetBytes(timestampParam).Uint64() % params.HistoricalRootsModulus
timestampIndex := libcommon.BigToHash(libcommon.Big256.SetUint64((timestampReduced)))
recordedTimestamp := uint256.NewInt(0)
root := uint256.NewInt(0)
state.GetState(libcommon.BytesToAddress(params.HistoryStorageAddress), &timestampIndex, recordedTimestamp)
recordedTimestampBytes := recordedTimestamp.Bytes32()
if !bytes.Equal(recordedTimestampBytes[:], timestampParam) {
return make([]byte, 32), nil
}
timestampExtended := timestampReduced + params.HistoricalRootsModulus
rootIndex := libcommon.BigToHash(libcommon.Big256.SetUint64((timestampExtended)))
state.GetState(libcommon.BytesToAddress(params.HistoryStorageAddress), &rootIndex, root)
res := root.Bytes32()
return res[:], nil
}

View File

@ -99,7 +99,7 @@ func testPrecompiled(t *testing.T, addr string, test precompiledTest) {
in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in)
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
if res, _, err := RunPrecompiledContract(p, in, gas, nil); err != nil {
if res, _, err := RunPrecompiledContract(p, in, gas); err != nil {
t.Error(err)
} else if common.Bytes2Hex(res) != test.Expected {
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
@ -121,7 +121,7 @@ func testPrecompiledOOG(t *testing.T, addr string, test precompiledTest) {
gas := p.RequiredGas(in) - 1
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
_, _, err := RunPrecompiledContract(p, in, gas, nil)
_, _, err := RunPrecompiledContract(p, in, gas)
if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err)
}
@ -138,7 +138,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing
in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in)
t.Run(test.Name, func(t *testing.T) {
_, _, err := RunPrecompiledContract(p, in, gas, nil)
_, _, err := RunPrecompiledContract(p, in, gas)
if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
}
@ -170,7 +170,7 @@ func benchmarkPrecompiled(b *testing.B, addr string, test precompiledTest) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
copy(data, in)
res, _, err = RunPrecompiledContract(p, data, reqGas, nil)
res, _, err = RunPrecompiledContract(p, data, reqGas)
}
bench.StopTimer()
elapsed := uint64(time.Since(start))

View File

@ -233,7 +233,7 @@ func (evm *EVM) call(typ OpCode, caller ContractRef, addr libcommon.Address, inp
// It is allowed to call precompiles, even via delegatecall
if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.intraBlockState)
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else if len(code) == 0 {
// If the account has no code, we can abort here
// The depth-check is already done, and precompiles handled above

View File

@ -42,16 +42,10 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
)
evm.IntraBlockState().GetState(contract.Address(), &slot, &current)
// Check slot presence in the access list
if addrPresent, slotPresent := evm.IntraBlockState().SlotInAccessList(contract.Address(), slot); !slotPresent {
if _, slotPresent := evm.IntraBlockState().SlotInAccessList(contract.Address(), slot); !slotPresent {
cost = params.ColdSloadCostEIP2929
// If the caller cannot afford the cost, this change will be rolled back
evm.IntraBlockState().AddSlotToAccessList(contract.Address(), slot)
if !addrPresent {
// Once we're done with YOLOv2 and schedule this for mainnet, might
// be good to remove this panic here, which is just really a
// canary to have during testing
panic("impossible case: address was not present in access list during sstore op")
}
}
var value uint256.Int
value.Set(y)

View File

@ -18,6 +18,8 @@ package params
import (
"math/big"
"github.com/ledgerwatch/erigon-lib/common"
)
const (
@ -168,14 +170,10 @@ const (
MinBlobGasPrice = 1
BlobGasPriceUpdateFraction = 3338477
PointEvaluationGas uint64 = 50000
//EIP-4788: Parent Beacon Root Precompile
ParentBeaconBlockRootGas uint64 = 4200
HistoricalRootsModulus uint64 = 98304
)
// EIP-4788: Storage address for parent beacon root
var HistoryStorageAddress = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0B}
// EIP-4788: Beacon block root in the EVM
var BeaconRootsAddress = common.HexToAddress("0x0b")
// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations
var Bls12381MultiExpDiscountTable = [128]uint64{1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, 408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, 281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222, 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 176, 176, 175, 174}

View File

@ -20,8 +20,7 @@ func TestExecutionSpec(t *testing.T) {
bt.skipLoad(`^homestead/`)
// TODO(yperbasis): fix me
bt.skipLoad(`^cancun/eip4844_blobs/`)
bt.skipLoad(`^cancun/eip6780_selfdestruct/`)
bt.skipLoad(`^cancun/`)
// TODO(yperbasis): re-enable checkStateRoot
checkStateRoot := false