Skip to content

Commit

Permalink
EIP-4788 v2 (no precompile) (#8038)
Browse files Browse the repository at this point in the history
See ethereum/EIPs#7456 &
ethereum/go-ethereum#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.
  • Loading branch information
yperbasis committed Aug 24, 2023
1 parent bb2c2ad commit 1fd9d20
Show file tree
Hide file tree
Showing 11 changed files with 40 additions and 195 deletions.
1 change: 0 additions & 1 deletion consensus/bor/bor.go
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion consensus/merge/merge.go
Expand Up @@ -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 */)
})
}
}

Expand Down
19 changes: 6 additions & 13 deletions consensus/misc/eip4788.go
@@ -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)
}
}
94 changes: 0 additions & 94 deletions consensus/misc/eip4788_test.go

This file was deleted.

9 changes: 7 additions & 2 deletions core/blockchain.go
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
77 changes: 13 additions & 64 deletions core/vm/contracts.go
Expand Up @@ -17,7 +17,6 @@
package vm

import (
"bytes"
"crypto/sha256"
"encoding/binary"
"errors"
Expand All @@ -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"
Expand All @@ -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{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
8 changes: 4 additions & 4 deletions core/vm/contracts_test.go
Expand Up @@ -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))
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion core/vm/evm.go
Expand Up @@ -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
Expand Down
8 changes: 1 addition & 7 deletions core/vm/operations_acl.go
Expand Up @@ -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)
Expand Down
10 changes: 4 additions & 6 deletions params/protocol_params.go
Expand Up @@ -18,6 +18,8 @@ package params

import (
"math/big"

"github.com/ledgerwatch/erigon-lib/common"
)

const (
Expand Down Expand Up @@ -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}
Expand Down
3 changes: 1 addition & 2 deletions tests/exec_spec_test.go
Expand Up @@ -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
Expand Down

0 comments on commit 1fd9d20

Please sign in to comment.