diff --git a/common/types.go b/common/types.go
index 93fb0904545e..b1de56073b6e 100644
--- a/common/types.go
+++ b/common/types.go
@@ -19,6 +19,7 @@ package common
import (
"bytes"
"database/sql/driver"
+ "encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
@@ -65,6 +66,13 @@ func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
// If b is larger than len(h), b will be cropped from the left.
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
+// Uint64ToHash sets the lowest 8 bytes of hash to u in big endian format.
+func Uint64ToHash(u uint64) Hash {
+ var h Hash
+ binary.BigEndian.PutUint64(h[24:], u)
+ return h
+}
+
// Less compares two hashes.
func (h Hash) Less(other Hash) bool {
return bytes.Compare(h[:], other[:]) < 0
diff --git a/consensus/misc/eip4788.go b/consensus/misc/eip4788.go
new file mode 100644
index 000000000000..6a1fae0115bb
--- /dev/null
+++ b/consensus/misc/eip4788.go
@@ -0,0 +1,47 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package misc
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// ApplyBeaconRoot adds the beacon root from the header to the state.
+func ApplyBeaconRoot(header *types.Header, state *state.StateDB) {
+ // If EIP-4788 is enabled, we need to store the block root
+ timeKey, time, rootKey, root := calcBeaconRootIndices(header)
+ state.SetState(params.BeaconRootsStorageAddress, timeKey, time)
+ state.SetState(params.BeaconRootsStorageAddress, rootKey, root)
+ // We also need to ensure that the BeaconRoot address has nonzero nonce.
+ if state.GetNonce(params.BeaconRootsStorageAddress) == 0 {
+ state.SetNonce(params.BeaconRootsStorageAddress, 1)
+ }
+}
+
+func calcBeaconRootIndices(header *types.Header) (timeKey, time, rootKey, root common.Hash) {
+ // timeKey -> header.Time
+ timeIndex := header.Time % params.HistoricalRootsModulus
+ timeKey = common.Uint64ToHash(timeIndex)
+ time = common.Uint64ToHash(header.Time)
+ // rootKey -> header.BeaconRoot
+ rootKey = common.Uint64ToHash(timeIndex + params.HistoricalRootsModulus)
+ root = *header.BeaconRoot
+ return
+}
diff --git a/consensus/misc/eip4788_test.go b/consensus/misc/eip4788_test.go
new file mode 100644
index 000000000000..5b1f581993af
--- /dev/null
+++ b/consensus/misc/eip4788_test.go
@@ -0,0 +1,63 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package misc
+
+import (
+ "encoding/json"
+ "os"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+type tcInput struct {
+ HeaderTime uint64
+ HeaderBeaconRoot common.Hash
+
+ TimeKey common.Hash
+ Time common.Hash
+ RootKey common.Hash
+ Root common.Hash
+}
+
+func TestCalcBeaconRootIndices(t *testing.T) {
+ data, err := os.ReadFile("./testdata/eip4788_beaconroot.json")
+ if err != nil {
+ t.Fatal(err)
+ }
+ var tests []tcInput
+ if err := json.Unmarshal(data, &tests); err != nil {
+ t.Fatal(err)
+ }
+ for i, tc := range tests {
+ header := types.Header{Time: tc.HeaderTime, BeaconRoot: &tc.HeaderBeaconRoot}
+ haveTimeKey, haveTime, haveRootKey, haveRoot := calcBeaconRootIndices(&header)
+ if haveTimeKey != tc.TimeKey {
+ t.Errorf("test %d: invalid time key: \nhave %v\nwant %v", i, haveTimeKey, tc.TimeKey)
+ }
+ if haveTime != tc.Time {
+ t.Errorf("test %d: invalid time: \nhave %v\nwant %v", i, haveTime, tc.Time)
+ }
+ if haveRootKey != tc.RootKey {
+ t.Errorf("test %d: invalid root key: \nhave %v\nwant %v", i, haveRootKey, tc.RootKey)
+ }
+ if haveRoot != tc.Root {
+ t.Errorf("test %d: invalid root: \nhave %v\nwant %v", i, haveRoot, tc.Root)
+ }
+ }
+}
diff --git a/consensus/misc/testdata/eip4788_beaconroot.json b/consensus/misc/testdata/eip4788_beaconroot.json
new file mode 100644
index 000000000000..3279d3ff5de0
--- /dev/null
+++ b/consensus/misc/testdata/eip4788_beaconroot.json
@@ -0,0 +1,26 @@
+[
+ {
+ "HeaderTime": 0,
+ "HeaderBeaconRoot": "0x0100000000000000000000000000000000000000000000000000000000000000",
+ "TimeKey": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "Time": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "RootKey": "0x0000000000000000000000000000000000000000000000000000000000018000",
+ "Root": "0x0100000000000000000000000000000000000000000000000000000000000000"
+ },
+ {
+ "HeaderTime": 120265298769267,
+ "HeaderBeaconRoot": "0xfffefc0000000000000000000000000000000000000000000000000000000000",
+ "TimeKey": "0x000000000000000000000000000000000000000000000000000000000000f573",
+ "Time": "0x00000000000000000000000000000000000000000000000000006d6172697573",
+ "RootKey": "0x0000000000000000000000000000000000000000000000000000000000027573",
+ "Root": "0xfffefc0000000000000000000000000000000000000000000000000000000000"
+ },
+ {
+ "HeaderTime": 18446744073709551615,
+ "HeaderBeaconRoot": "0xfffefcffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "TimeKey": "0x000000000000000000000000000000000000000000000000000000000000ffff",
+ "Time": "0x000000000000000000000000000000000000000000000000ffffffffffffffff",
+ "RootKey": "0x0000000000000000000000000000000000000000000000000000000000027fff",
+ "Root": "0xfffefcffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ }
+]
\ No newline at end of file
diff --git a/core/state_processor.go b/core/state_processor.go
index fcaf5a8ff3c9..2431eb937eef 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -71,6 +71,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb)
}
+ if p.config.IsCancun(blockNumber, block.Time()) {
+ if header.BeaconRoot == nil {
+ return nil, nil, 0, errors.New("expected beacon root post-cancun")
+ }
+ misc.ApplyBeaconRoot(header, statedb)
+ } else if header.BeaconRoot != nil {
+ return nil, nil, 0, errors.New("beacon root set pre-cancun")
+ }
+
var (
context = NewEVMBlockContext(header, p.bc, nil)
vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
diff --git a/core/types/block.go b/core/types/block.go
index 4452f4bb21f8..1ca822323337 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -90,6 +90,9 @@ type Header struct {
// ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers.
ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"`
+
+ // BeaconRoot was added by EIP-4788 and is ignored in legacy headers.
+ BeaconRoot *common.Hash `json:"beaconRoot" rlp:"optional"`
}
// field type overrides for gencodec
@@ -378,6 +381,10 @@ func (b *Block) BlobGasUsed() *uint64 {
return blobGasUsed
}
+func (b *Block) BeaconRoot() *common.Hash {
+ return b.header.BeaconRoot
+}
+
func (b *Block) Header() *Header { return CopyHeader(b.header) }
// Body returns the non-header content of the block.
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 6041be6c9f49..f3455c69d51e 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -34,12 +34,16 @@ import (
"golang.org/x/crypto/ripemd160"
)
+type StateReader interface {
+ GetState(common.Address, common.Hash) common.Hash
+}
+
// PrecompiledContract is the basic interface for native Go contracts. The implementation
// requires a deterministic gas count based on the input size of the Run method of the
// contract.
type PrecompiledContract interface {
- RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
- Run(input []byte) ([]byte, error) // Run runs the precompiled contract
+ RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
+ Run(statedb StateReader, input []byte) ([]byte, error) // Run runs the precompiled contract
}
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
@@ -105,6 +109,7 @@ var PrecompiledContractsCancun = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{20}): &kzgPointEvaluation{},
+ params.BeaconRootsStorageAddress: &beaconRoot{},
}
// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
@@ -168,13 +173,13 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
// - the returned bytes,
// - the _remaining_ gas,
// - any error that occurred
-func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
+func RunPrecompiledContract(stateDB StateReader, 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
- output, err := p.Run(input)
+ output, err := p.Run(stateDB, input)
return output, suppliedGas, err
}
@@ -185,7 +190,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}
-func (c *ecrecover) Run(input []byte) ([]byte, error) {
+func (c *ecrecover) Run(stateDB StateReader, input []byte) ([]byte, error) {
const ecRecoverInputLength = 128
input = common.RightPadBytes(input, ecRecoverInputLength)
@@ -226,7 +231,7 @@ type sha256hash struct{}
func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}
-func (c *sha256hash) Run(input []byte) ([]byte, error) {
+func (c *sha256hash) Run(stateDB StateReader, input []byte) ([]byte, error) {
h := sha256.Sum256(input)
return h[:], nil
}
@@ -241,7 +246,7 @@ type ripemd160hash struct{}
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}
-func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
+func (c *ripemd160hash) Run(stateDB StateReader, input []byte) ([]byte, error) {
ripemd := ripemd160.New()
ripemd.Write(input)
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
@@ -257,8 +262,8 @@ type dataCopy struct{}
func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}
-func (c *dataCopy) Run(in []byte) ([]byte, error) {
- return common.CopyBytes(in), nil
+func (c *dataCopy) Run(stateDB StateReader, input []byte) ([]byte, error) {
+ return common.CopyBytes(input), nil
}
// bigModExp implements a native big integer exponential modular operation.
@@ -383,7 +388,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
return gas.Uint64()
}
-func (c *bigModExp) Run(input []byte) ([]byte, error) {
+func (c *bigModExp) Run(stateDB StateReader, input []byte) ([]byte, error) {
var (
baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
@@ -463,7 +468,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasIstanbul
}
-func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) {
+func (c *bn256AddIstanbul) Run(stateDB StateReader, input []byte) ([]byte, error) {
return runBn256Add(input)
}
@@ -476,7 +481,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasByzantium
}
-func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) {
+func (c *bn256AddByzantium) Run(stateDB StateReader, input []byte) ([]byte, error) {
return runBn256Add(input)
}
@@ -501,7 +506,7 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasIstanbul
}
-func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) {
+func (c *bn256ScalarMulIstanbul) Run(stateDB StateReader, input []byte) ([]byte, error) {
return runBn256ScalarMul(input)
}
@@ -514,7 +519,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasByzantium
}
-func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) {
+func (c *bn256ScalarMulByzantium) Run(stateDB StateReader, input []byte) ([]byte, error) {
return runBn256ScalarMul(input)
}
@@ -569,7 +574,7 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul
}
-func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) {
+func (c *bn256PairingIstanbul) Run(stateDB StateReader, input []byte) ([]byte, error) {
return runBn256Pairing(input)
}
@@ -582,7 +587,7 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium
}
-func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
+func (c *bn256PairingByzantium) Run(stateDB StateReader, input []byte) ([]byte, error) {
return runBn256Pairing(input)
}
@@ -608,7 +613,7 @@ var (
errBlake2FInvalidFinalFlag = errors.New("invalid final flag")
)
-func (c *blake2F) Run(input []byte) ([]byte, error) {
+func (c *blake2F) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Make sure the input is valid (correct length and final flag)
if len(input) != blake2FInputLength {
return nil, errBlake2FInvalidInputLength
@@ -662,7 +667,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G1AddGas
}
-func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
+func (c *bls12381G1Add) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Implements EIP-2537 G1Add precompile.
// > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each).
// > Output is an encoding of addition operation result - single G1 point (`128` bytes).
@@ -700,7 +705,7 @@ func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 {
return params.Bls12381G1MulGas
}
-func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) {
+func (c *bls12381G1Mul) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Implements EIP-2537 G1Mul precompile.
// > G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
// > Output is an encoding of multiplication operation result - single G1 point (`128` bytes).
@@ -750,7 +755,7 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
}
-func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
+func (c *bls12381G1MultiExp) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Implements EIP-2537 G1MultiExp precompile.
// G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
// Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes).
@@ -793,7 +798,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G2AddGas
}
-func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
+func (c *bls12381G2Add) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Implements EIP-2537 G2Add precompile.
// > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each).
// > Output is an encoding of addition operation result - single G2 point (`256` bytes).
@@ -831,7 +836,7 @@ func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 {
return params.Bls12381G2MulGas
}
-func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) {
+func (c *bls12381G2Mul) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Implements EIP-2537 G2MUL precompile logic.
// > G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
// > Output is an encoding of multiplication operation result - single G2 point (`256` bytes).
@@ -881,7 +886,7 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
}
-func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
+func (c *bls12381G2MultiExp) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Implements EIP-2537 G2MultiExp precompile logic
// > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
// > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes).
@@ -924,7 +929,7 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 {
return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas
}
-func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
+func (c *bls12381Pairing) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Implements EIP-2537 Pairing precompile logic.
// > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure:
// > - `128` bytes of G1 point encoding
@@ -1003,7 +1008,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG1Gas
}
-func (c *bls12381MapG1) Run(input []byte) ([]byte, error) {
+func (c *bls12381MapG1) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Implements EIP-2537 Map_To_G1 precompile.
// > Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field.
// > Output of this call is `128` bytes and is G1 point following respective encoding rules.
@@ -1038,7 +1043,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG2Gas
}
-func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
+func (c *bls12381MapG2) Run(stateDB StateReader, input []byte) ([]byte, error) {
// Implements EIP-2537 Map_FP2_TO_G2 precompile logic.
// > Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field.
// > Output of this call is `256` bytes and is G2 point following respective encoding rules.
@@ -1093,7 +1098,7 @@ var (
)
// Run executes the point evaluation precompile.
-func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) {
+func (b *kzgPointEvaluation) Run(stateDB StateReader, input []byte) ([]byte, error) {
if len(input) != blobVerifyInputLength {
return nil, errBlobVerifyInvalidInputLength
}
@@ -1135,3 +1140,28 @@ func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash {
return h
}
+
+// BeaconRoot is a stateful precompile that returns a beacon root.
+type beaconRoot struct{}
+
+func (c *beaconRoot) RequiredGas(input []byte) uint64 {
+ return params.BeaconRootPrecompileGas
+}
+
+func (c *beaconRoot) Run(stateDB StateReader, input []byte) ([]byte, error) {
+ if len(input) != common.HashLength {
+ return nil, errors.New("invalid input length")
+ }
+ var timestamp common.Hash
+ copy(timestamp[:], input)
+ // retrieve stored timestamp
+ timeIndex := binary.BigEndian.Uint64(timestamp[24:]) % params.HistoricalRootsModulus
+ recordedTimestamp := stateDB.GetState(params.BeaconRootsStorageAddress, common.Uint64ToHash(timeIndex))
+ if recordedTimestamp != timestamp {
+ return make([]byte, 32), nil
+ }
+ // retrieve stored beacon root
+ rootIndex := timeIndex + params.HistoricalRootsModulus
+ beaconRoot := stateDB.GetState(params.BeaconRootsStorageAddress, common.Uint64ToHash(rootIndex))
+ return beaconRoot[:], nil
+}
diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go
index 5b1e874e91b4..d2507a112395 100644
--- a/core/vm/contracts_test.go
+++ b/core/vm/contracts_test.go
@@ -97,7 +97,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
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); err != nil {
+ if res, _, err := RunPrecompiledContract(nil, 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))
@@ -119,7 +119,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
gas := p.RequiredGas(in) - 1
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
- _, _, err := RunPrecompiledContract(p, in, gas)
+ _, _, err := RunPrecompiledContract(nil, p, in, gas)
if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err)
}
@@ -136,7 +136,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)
+ _, _, err := RunPrecompiledContract(nil, p, in, gas)
if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
}
@@ -168,7 +168,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
copy(data, in)
- res, _, err = RunPrecompiledContract(p, data, reqGas)
+ res, _, err = RunPrecompiledContract(nil, p, data, reqGas)
}
bench.StopTimer()
elapsed := uint64(time.Since(start))
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 40e2f3554f46..eb048bcf8d04 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -220,7 +220,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
if isPrecompile {
- ret, gas, err = RunPrecompiledContract(p, input, gas)
+ ret, gas, err = RunPrecompiledContract(evm.StateDB, p, input, gas)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
@@ -283,7 +283,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
- ret, gas, err = RunPrecompiledContract(p, input, gas)
+ ret, gas, err = RunPrecompiledContract(evm.StateDB, p, input, gas)
} else {
addrCopy := addr
// Initialise a new contract and set the code that is to be used by the EVM.
@@ -328,7 +328,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
- ret, gas, err = RunPrecompiledContract(p, input, gas)
+ ret, gas, err = RunPrecompiledContract(evm.StateDB, p, input, gas)
} else {
addrCopy := addr
// Initialise a new contract and make initialise the delegate values
@@ -377,7 +377,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
}
if p, isPrecompile := evm.precompile(addr); isPrecompile {
- ret, gas, err = RunPrecompiledContract(p, input, gas)
+ ret, gas, err = RunPrecompiledContract(evm.StateDB, p, input, gas)
} else {
// At this point, we use a copy of address. If we don't, the go compiler will
// leak the 'contract' to the outer scope, and make allocation for 'contract'
diff --git a/params/protocol_params.go b/params/protocol_params.go
index b5c57789be2d..e84563e4b42a 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.
@@ -160,6 +164,9 @@ const (
RefundQuotient uint64 = 2
RefundQuotientEIP3529 uint64 = 5
+ HistoricalRootsModulus uint64 = 98304 // Limits how many historical roots are stored by EIP-4788
+ BeaconRootPrecompileGas uint64 = 4200 // Price for retrieving a beacon root from the beacon root precompile
+
BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element
BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob
BlobTxHashVersion = 0x01 // Version byte of the commitment hash
@@ -179,4 +186,6 @@ 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.
+
+ BeaconRootsStorageAddress common.Address = common.BytesToAddress([]byte{0xb}) // Address where historical beacon roots are stored as per EIP-4788
)
diff --git a/tests/fuzzers/bls12381/precompile_fuzzer.go b/tests/fuzzers/bls12381/precompile_fuzzer.go
index cab2bcba3863..f1083ad7a888 100644
--- a/tests/fuzzers/bls12381/precompile_fuzzer.go
+++ b/tests/fuzzers/bls12381/precompile_fuzzer.go
@@ -92,7 +92,7 @@ func fuzz(id byte, data []byte) int {
}
cpy := make([]byte, len(data))
copy(cpy, data)
- _, err := precompile.Run(cpy)
+ _, err := precompile.Run(nil, cpy)
if !bytes.Equal(cpy, data) {
panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy))
}