mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 03:30:37 +00:00
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:
parent
bb2c2adbb6
commit
1fd9d20e14
@ -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
|
||||
|
@ -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 */)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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, ×tampIndex, *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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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
|
||||
|
@ -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), ×tampIndex, 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
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -42,16 +42,10 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||
)
|
||||
evm.IntraBlockState().GetState(contract.Address(), &slot, ¤t)
|
||||
// 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)
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user