From e8510022cf5622fd4991de4d7165fdd55352826a Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 17 May 2023 11:50:29 +0200 Subject: [PATCH 01/17] core: implement modified version of EIP-4788 --- core/state_processor.go | 3 +++ core/types/block.go | 3 +++ core/vm/eips.go | 11 +++++++++++ core/vm/instructions.go | 11 +++++++++++ core/vm/opcodes.go | 1 + 5 files changed, 29 insertions(+) diff --git a/core/state_processor.go b/core/state_processor.go index 4837628e6602..a30fc341430d 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -71,6 +71,9 @@ 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 header.BeaconRoot != nil { + misc.ApplyBeaconRoot(header, statedb) + } 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 e1f1feb7a2ec..eb5e2b14f9cc 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -90,6 +90,9 @@ type Header struct { // DataGasUsed was added by EIP-4844 and is ignored in legacy headers. DataGasUsed *uint64 `json:"dataGasUsed" 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 diff --git a/core/vm/eips.go b/core/vm/eips.go index ff1f132cb355..d018124c4207 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -218,6 +218,17 @@ func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] return nil, nil } +// enable4788 applies EIP-4788 (BEACONROOT opcode) +func enable4788(jt *JumpTable) { + // New opcode + jt[BEACONROOT] = &operation{ + execute: opBeaconRoot, + constantGas: GasExtStep, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } +} + // enable3855 applies EIP-3855 (PUSH0 opcode) func enable3855(jt *JumpTable) { // New opcode diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 505aef412775..f13c5f9ad837 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -452,6 +452,17 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( return nil, nil } +func opBeaconRoot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + loc := scope.Stack.peek() + slotsPerHistoricalRoot := 8192 + loc = loc.Mod(loc, uint256.NewInt(uint64(slotsPerHistoricalRoot))) // TODO might be unsafe + hash := common.Hash(loc.Bytes32()) + historicalStorageAddress := common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffd") + val := interpreter.evm.StateDB.GetState(historicalStorageAddress, hash) + loc.SetBytes(val.Bytes()) + return nil, nil +} + func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) return nil, nil diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 3cdeab8ff919..268e1df4e32b 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -101,6 +101,7 @@ const ( SELFBALANCE OpCode = 0x47 BASEFEE OpCode = 0x48 BLOBHASH OpCode = 0x49 + BEACONROOT OpCode = 0x4A ) // 0x50 range - 'storage' and execution. From 6f11378ba488b6138066a88df845b148bc30b447 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 17 May 2023 13:47:37 +0200 Subject: [PATCH 02/17] consensus/misc: add eip4788 --- consensus/misc/eip4788.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 consensus/misc/eip4788.go diff --git a/consensus/misc/eip4788.go b/consensus/misc/eip4788.go new file mode 100644 index 000000000000..75603328c5fb --- /dev/null +++ b/consensus/misc/eip4788.go @@ -0,0 +1,17 @@ +package misc + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" +) + +func ApplyBeaconRoot(header *types.Header, state *state.StateDB) { + // If EIP-4788 is enabled, we need to store the block root + historicalStorageAddress := common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffd") + key := header.Time + value := header.BeaconRoot + state.SetState(historicalStorageAddress, common.BigToHash(big.NewInt(int64(key))), *value) +} From d6d0d6d24e045dbd485628cb9bbf02cdba171a7f Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 19 May 2023 17:03:56 +0200 Subject: [PATCH 03/17] consensus/misc: header --- consensus/misc/eip4788.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/consensus/misc/eip4788.go b/consensus/misc/eip4788.go index 75603328c5fb..b2e030c8a969 100644 --- a/consensus/misc/eip4788.go +++ b/consensus/misc/eip4788.go @@ -1,3 +1,19 @@ +// 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 ( @@ -8,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) +// 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 historicalStorageAddress := common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffd") From 10390cf9fe362674b4e9af3f3af02bc86166ae36 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 19 May 2023 17:13:43 +0200 Subject: [PATCH 04/17] core/vm: allow for stateful precompiles --- core/vm/contracts.go | 52 +++++++++++++++++++-------------------- core/vm/contracts_test.go | 8 +++--- core/vm/evm.go | 8 +++--- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 6041be6c9f49..f9eda7be08d2 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -38,8 +38,8 @@ import ( // 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 StateDB, input []byte) ([]byte, error) // Run runs the precompiled contract } // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum @@ -168,13 +168,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 StateDB, 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 +185,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(input []byte) ([]byte, error) { +func (c *ecrecover) Run(stateDB StateDB, input []byte) ([]byte, error) { const ecRecoverInputLength = 128 input = common.RightPadBytes(input, ecRecoverInputLength) @@ -226,7 +226,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 StateDB, input []byte) ([]byte, error) { h := sha256.Sum256(input) return h[:], nil } @@ -241,7 +241,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 StateDB, input []byte) ([]byte, error) { ripemd := ripemd160.New() ripemd.Write(input) return common.LeftPadBytes(ripemd.Sum(nil), 32), nil @@ -257,8 +257,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 StateDB, input []byte) ([]byte, error) { + return common.CopyBytes(input), nil } // bigModExp implements a native big integer exponential modular operation. @@ -383,7 +383,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } -func (c *bigModExp) Run(input []byte) ([]byte, error) { +func (c *bigModExp) Run(stateDB StateDB, 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 +463,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul } -func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256AddIstanbul) Run(stateDB StateDB, input []byte) ([]byte, error) { return runBn256Add(input) } @@ -476,7 +476,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium } -func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256AddByzantium) Run(stateDB StateDB, input []byte) ([]byte, error) { return runBn256Add(input) } @@ -501,7 +501,7 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul } -func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) { +func (c *bn256ScalarMulIstanbul) Run(stateDB StateDB, input []byte) ([]byte, error) { return runBn256ScalarMul(input) } @@ -514,7 +514,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium } -func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) { +func (c *bn256ScalarMulByzantium) Run(stateDB StateDB, input []byte) ([]byte, error) { return runBn256ScalarMul(input) } @@ -569,7 +569,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 StateDB, input []byte) ([]byte, error) { return runBn256Pairing(input) } @@ -582,7 +582,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 StateDB, input []byte) ([]byte, error) { return runBn256Pairing(input) } @@ -608,7 +608,7 @@ var ( errBlake2FInvalidFinalFlag = errors.New("invalid final flag") ) -func (c *blake2F) Run(input []byte) ([]byte, error) { +func (c *blake2F) Run(stateDB StateDB, input []byte) ([]byte, error) { // Make sure the input is valid (correct length and final flag) if len(input) != blake2FInputLength { return nil, errBlake2FInvalidInputLength @@ -662,7 +662,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas } -func (c *bls12381G1Add) Run(input []byte) ([]byte, error) { +func (c *bls12381G1Add) Run(stateDB StateDB, 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 +700,7 @@ func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G1MulGas } -func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) { +func (c *bls12381G1Mul) Run(stateDB StateDB, 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 +750,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 StateDB, 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 +793,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas } -func (c *bls12381G2Add) Run(input []byte) ([]byte, error) { +func (c *bls12381G2Add) Run(stateDB StateDB, 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 +831,7 @@ func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G2MulGas } -func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) { +func (c *bls12381G2Mul) Run(stateDB StateDB, 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 +881,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 StateDB, 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 +924,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 StateDB, 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 +1003,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas } -func (c *bls12381MapG1) Run(input []byte) ([]byte, error) { +func (c *bls12381MapG1) Run(stateDB StateDB, 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 +1038,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas } -func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { +func (c *bls12381MapG2) Run(stateDB StateDB, 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. 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 36336d8cbd4c..1213d928a812 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -222,7 +222,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. @@ -285,7 +285,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. @@ -330,7 +330,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 @@ -379,7 +379,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' From 3d4ecc3df5e21ddabee784fdc48d496b7e9be7f8 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 19 May 2023 17:22:58 +0200 Subject: [PATCH 05/17] core/vm: implement 4788 as a stateful precompile --- core/vm/contracts.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index f9eda7be08d2..ea792ad31064 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -121,6 +121,19 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{18}): &bls12381MapG2{}, } +var PrecompiledContracts4788 = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{20}): &beaconRoot{}, +} + var ( PrecompiledAddressesCancun []common.Address PrecompiledAddressesBerlin []common.Address @@ -1135,3 +1148,21 @@ 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 20 // TODO (MariusVanDerWijden) change +} + +func (c *beaconRoot) Run(stateDB StateDB, input []byte) ([]byte, error) { + if len(input) != common.HashLength { + return nil, errors.New("invalid input length") + } + var root common.Hash + copy(root[:], input) + historicalStorageAddress := common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffd") + val := stateDB.GetState(historicalStorageAddress, root) + return val[:], nil +} From f115bf6ced249f16140d96f614499ffa8c97ba75 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 29 Jun 2023 09:28:59 +0200 Subject: [PATCH 06/17] core, consensus/misc: update to new 4788 spec --- common/types.go | 8 ++++++++ consensus/misc/eip4788.go | 15 +++++++++------ core/vm/contracts.go | 21 +++++++++++++++------ params/protocol_params.go | 2 ++ tests/fuzzers/bls12381/precompile_fuzzer.go | 2 +- 5 files changed, 35 insertions(+), 13 deletions(-) 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 index b2e030c8a969..1b7c40b2ee6d 100644 --- a/consensus/misc/eip4788.go +++ b/consensus/misc/eip4788.go @@ -17,18 +17,21 @@ package misc import ( - "math/big" - "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 - historicalStorageAddress := common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffd") - key := header.Time - value := header.BeaconRoot - state.SetState(historicalStorageAddress, common.BigToHash(big.NewInt(int64(key))), *value) + historicalStorageAddress := common.BytesToAddress([]byte{20}) + timeIndex := header.Time % params.HistoricalRootModulus + rootIndex := timeIndex + params.HistoricalRootModulus + time := header.Time + // timeIndex -> header.Time + state.SetState(historicalStorageAddress, common.Uint64ToHash(timeIndex), common.Uint64ToHash(time)) + // rootIndex -> header.BeaconRoot + state.SetState(historicalStorageAddress, common.Uint64ToHash(rootIndex), *header.BeaconRoot) } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index ea792ad31064..c2c79be4b2a3 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -1152,17 +1152,26 @@ func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash { // BeaconRoot is a stateful precompile that returns a beacon root. type beaconRoot struct{} +var beaconRootStorageAddress = common.BytesToAddress([]byte{20}) + func (c *beaconRoot) RequiredGas(input []byte) uint64 { - return 20 // TODO (MariusVanDerWijden) change + return 4200 } func (c *beaconRoot) Run(stateDB StateDB, input []byte) ([]byte, error) { if len(input) != common.HashLength { return nil, errors.New("invalid input length") } - var root common.Hash - copy(root[:], input) - historicalStorageAddress := common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffd") - val := stateDB.GetState(historicalStorageAddress, root) - return val[:], nil + var timestamp common.Hash + copy(timestamp[:], input) + // retrieve stored timestamp + timeIndex := binary.BigEndian.Uint64(timestamp[24:]) % params.HistoricalRootModulus + recordedTimestamp := stateDB.GetState(beaconRootStorageAddress, common.Uint64ToHash(timeIndex)) + if recordedTimestamp != timestamp { + return make([]byte, 32), nil + } + // retrieve stored beacon root + rootIndex := timeIndex + params.HistoricalRootModulus + beaconRoot := stateDB.GetState(beaconRootStorageAddress, common.Uint64ToHash(rootIndex)) + return beaconRoot[:], nil } diff --git a/params/protocol_params.go b/params/protocol_params.go index 9a0b8115b13c..c01d2c52e3b7 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -160,6 +160,8 @@ const ( RefundQuotient uint64 = 2 RefundQuotientEIP3529 uint64 = 5 + HistoricalRootModulus = 98304 // Limits how many historical roots are stored by EIP-4788 + 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 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)) } From 2bd010219ee1397e1c6e4590aa795b673f98401f Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 29 Jun 2023 10:25:32 +0200 Subject: [PATCH 07/17] core: fix rebase --- core/vm/contracts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index c2c79be4b2a3..7e59ffa5abcb 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -1106,7 +1106,7 @@ var ( ) // Run executes the point evaluation precompile. -func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) { +func (b *kzgPointEvaluation) Run(stateDB StateDB, input []byte) ([]byte, error) { if len(input) != blobVerifyInputLength { return nil, errBlobVerifyInvalidInputLength } From bd221a9e9cc298d50929e4384fb221a650843b19 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 29 Jun 2023 11:09:29 +0200 Subject: [PATCH 08/17] core/vm: add activator --- core/vm/eips.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/vm/eips.go b/core/vm/eips.go index d018124c4207..b3ec0b48edf4 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -26,6 +26,7 @@ import ( ) var activators = map[int]func(*JumpTable){ + 4788: enable4788, 3855: enable3855, 3860: enable3860, 3529: enable3529, From 4a0845093397ff9b05a22850435f4cfcbfd60063 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 7 Jul 2023 10:35:40 +0200 Subject: [PATCH 09/17] core/vm: reduce interface, increase testability, remove opcode --- consensus/misc/eip4788.go | 22 ++++++++---- core/vm/contracts.go | 76 ++++++++++++++++++++------------------- core/vm/eips.go | 12 ------- core/vm/instructions.go | 11 ------ 4 files changed, 55 insertions(+), 66 deletions(-) diff --git a/consensus/misc/eip4788.go b/consensus/misc/eip4788.go index 1b7c40b2ee6d..4094cfc6d201 100644 --- a/consensus/misc/eip4788.go +++ b/consensus/misc/eip4788.go @@ -23,15 +23,23 @@ import ( "github.com/ethereum/go-ethereum/params" ) +var historicalStorageAddress = common.BytesToAddress([]byte{0xB}) + // 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 - historicalStorageAddress := common.BytesToAddress([]byte{20}) + timeKey, time, rootKey, root := calcBeaconRootIndices(header) + state.SetState(historicalStorageAddress, timeKey, time) + state.SetState(historicalStorageAddress, rootKey, root) +} + +func calcBeaconRootIndices(header *types.Header) (timeKey, time, rootKey, root common.Hash) { + // timeKey -> header.Time timeIndex := header.Time % params.HistoricalRootModulus - rootIndex := timeIndex + params.HistoricalRootModulus - time := header.Time - // timeIndex -> header.Time - state.SetState(historicalStorageAddress, common.Uint64ToHash(timeIndex), common.Uint64ToHash(time)) - // rootIndex -> header.BeaconRoot - state.SetState(historicalStorageAddress, common.Uint64ToHash(rootIndex), *header.BeaconRoot) + timeKey = common.Uint64ToHash(timeIndex) + time = common.Uint64ToHash(header.Time) + // rootKey -> header.BeaconRoot + rootKey = common.Uint64ToHash(timeIndex + params.HistoricalRootModulus) + root = *header.BeaconRoot + return } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 7e59ffa5abcb..6d6aca3bbea8 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(statedb StateDB, 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 @@ -122,16 +126,16 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ } var PrecompiledContracts4788 = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, - common.BytesToAddress([]byte{20}): &beaconRoot{}, + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{0xB}): &beaconRoot{}, } var ( @@ -181,7 +185,7 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(stateDB StateDB, 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 @@ -198,7 +202,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (c *ecrecover) Run(stateDB StateReader, input []byte) ([]byte, error) { const ecRecoverInputLength = 128 input = common.RightPadBytes(input, ecRecoverInputLength) @@ -239,7 +243,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(stateDB StateDB, input []byte) ([]byte, error) { +func (c *sha256hash) Run(stateDB StateReader, input []byte) ([]byte, error) { h := sha256.Sum256(input) return h[:], nil } @@ -254,7 +258,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(stateDB StateDB, 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 @@ -270,7 +274,7 @@ type dataCopy struct{} func (c *dataCopy) RequiredGas(input []byte) uint64 { return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas } -func (c *dataCopy) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (c *dataCopy) Run(stateDB StateReader, input []byte) ([]byte, error) { return common.CopyBytes(input), nil } @@ -396,7 +400,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } -func (c *bigModExp) Run(stateDB StateDB, 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() @@ -476,7 +480,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256AddGasIstanbul } -func (c *bn256AddIstanbul) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (c *bn256AddIstanbul) Run(stateDB StateReader, input []byte) ([]byte, error) { return runBn256Add(input) } @@ -489,7 +493,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 { return params.Bn256AddGasByzantium } -func (c *bn256AddByzantium) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (c *bn256AddByzantium) Run(stateDB StateReader, input []byte) ([]byte, error) { return runBn256Add(input) } @@ -514,7 +518,7 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasIstanbul } -func (c *bn256ScalarMulIstanbul) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (c *bn256ScalarMulIstanbul) Run(stateDB StateReader, input []byte) ([]byte, error) { return runBn256ScalarMul(input) } @@ -527,7 +531,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 { return params.Bn256ScalarMulGasByzantium } -func (c *bn256ScalarMulByzantium) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (c *bn256ScalarMulByzantium) Run(stateDB StateReader, input []byte) ([]byte, error) { return runBn256ScalarMul(input) } @@ -582,7 +586,7 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul } -func (c *bn256PairingIstanbul) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (c *bn256PairingIstanbul) Run(stateDB StateReader, input []byte) ([]byte, error) { return runBn256Pairing(input) } @@ -595,7 +599,7 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium } -func (c *bn256PairingByzantium) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (c *bn256PairingByzantium) Run(stateDB StateReader, input []byte) ([]byte, error) { return runBn256Pairing(input) } @@ -621,7 +625,7 @@ var ( errBlake2FInvalidFinalFlag = errors.New("invalid final flag") ) -func (c *blake2F) Run(stateDB StateDB, 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 @@ -675,7 +679,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 { return params.Bls12381G1AddGas } -func (c *bls12381G1Add) Run(stateDB StateDB, 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). @@ -713,7 +717,7 @@ func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G1MulGas } -func (c *bls12381G1Mul) Run(stateDB StateDB, 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). @@ -763,7 +767,7 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000 } -func (c *bls12381G1MultiExp) Run(stateDB StateDB, 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). @@ -806,7 +810,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 { return params.Bls12381G2AddGas } -func (c *bls12381G2Add) Run(stateDB StateDB, 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). @@ -844,7 +848,7 @@ func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 { return params.Bls12381G2MulGas } -func (c *bls12381G2Mul) Run(stateDB StateDB, 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). @@ -894,7 +898,7 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 { return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000 } -func (c *bls12381G2MultiExp) Run(stateDB StateDB, 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). @@ -937,7 +941,7 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 { return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas } -func (c *bls12381Pairing) Run(stateDB StateDB, 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 @@ -1016,7 +1020,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 { return params.Bls12381MapG1Gas } -func (c *bls12381MapG1) Run(stateDB StateDB, 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. @@ -1051,7 +1055,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 { return params.Bls12381MapG2Gas } -func (c *bls12381MapG2) Run(stateDB StateDB, 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. @@ -1106,7 +1110,7 @@ var ( ) // Run executes the point evaluation precompile. -func (b *kzgPointEvaluation) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (b *kzgPointEvaluation) Run(stateDB StateReader, input []byte) ([]byte, error) { if len(input) != blobVerifyInputLength { return nil, errBlobVerifyInvalidInputLength } @@ -1158,7 +1162,7 @@ func (c *beaconRoot) RequiredGas(input []byte) uint64 { return 4200 } -func (c *beaconRoot) Run(stateDB StateDB, input []byte) ([]byte, error) { +func (c *beaconRoot) Run(stateDB StateReader, input []byte) ([]byte, error) { if len(input) != common.HashLength { return nil, errors.New("invalid input length") } diff --git a/core/vm/eips.go b/core/vm/eips.go index b3ec0b48edf4..ff1f132cb355 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -26,7 +26,6 @@ import ( ) var activators = map[int]func(*JumpTable){ - 4788: enable4788, 3855: enable3855, 3860: enable3860, 3529: enable3529, @@ -219,17 +218,6 @@ func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] return nil, nil } -// enable4788 applies EIP-4788 (BEACONROOT opcode) -func enable4788(jt *JumpTable) { - // New opcode - jt[BEACONROOT] = &operation{ - execute: opBeaconRoot, - constantGas: GasExtStep, - minStack: minStack(1, 1), - maxStack: maxStack(1, 1), - } -} - // enable3855 applies EIP-3855 (PUSH0 opcode) func enable3855(jt *JumpTable) { // New opcode diff --git a/core/vm/instructions.go b/core/vm/instructions.go index f13c5f9ad837..505aef412775 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -452,17 +452,6 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( return nil, nil } -func opBeaconRoot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - loc := scope.Stack.peek() - slotsPerHistoricalRoot := 8192 - loc = loc.Mod(loc, uint256.NewInt(uint64(slotsPerHistoricalRoot))) // TODO might be unsafe - hash := common.Hash(loc.Bytes32()) - historicalStorageAddress := common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffd") - val := interpreter.evm.StateDB.GetState(historicalStorageAddress, hash) - loc.SetBytes(val.Bytes()) - return nil, nil -} - func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) return nil, nil From 564bfeefce8272fb2c8661666929f2a3f0e1a4ad Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 7 Jul 2023 10:41:07 +0200 Subject: [PATCH 10/17] params: introduce params.BeaconRootStorageAddress --- consensus/misc/eip4788.go | 2 +- core/vm/contracts.go | 22 +++++++++++----------- core/vm/opcodes.go | 1 - params/protocol_params.go | 3 ++- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/consensus/misc/eip4788.go b/consensus/misc/eip4788.go index 4094cfc6d201..1f8d0ad07f78 100644 --- a/consensus/misc/eip4788.go +++ b/consensus/misc/eip4788.go @@ -23,7 +23,7 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var historicalStorageAddress = common.BytesToAddress([]byte{0xB}) +var historicalStorageAddress = common.BytesToAddress([]byte{params.BeaconRootStorageAddress}) // ApplyBeaconRoot adds the beacon root from the header to the state. func ApplyBeaconRoot(header *types.Header, state *state.StateDB) { diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 6d6aca3bbea8..5f36b6890479 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -126,16 +126,16 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ } var PrecompiledContracts4788 = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, - common.BytesToAddress([]byte{0xB}): &beaconRoot{}, + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, + common.BytesToAddress([]byte{params.BeaconRootStorageAddress}): &beaconRoot{}, } var ( @@ -1156,7 +1156,7 @@ func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash { // BeaconRoot is a stateful precompile that returns a beacon root. type beaconRoot struct{} -var beaconRootStorageAddress = common.BytesToAddress([]byte{20}) +var beaconRootStorageAddress = common.BytesToAddress([]byte{params.BeaconRootStorageAddress}) func (c *beaconRoot) RequiredGas(input []byte) uint64 { return 4200 diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 268e1df4e32b..3cdeab8ff919 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -101,7 +101,6 @@ const ( SELFBALANCE OpCode = 0x47 BASEFEE OpCode = 0x48 BLOBHASH OpCode = 0x49 - BEACONROOT OpCode = 0x4A ) // 0x50 range - 'storage' and execution. diff --git a/params/protocol_params.go b/params/protocol_params.go index c01d2c52e3b7..b8444793cae6 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -160,7 +160,8 @@ const ( RefundQuotient uint64 = 2 RefundQuotientEIP3529 uint64 = 5 - HistoricalRootModulus = 98304 // Limits how many historical roots are stored by EIP-4788 + BeaconRootStorageAddress byte = 0x0B + HistoricalRootModulus = 98304 // Limits how many historical roots are stored by EIP-4788 BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob From 8cf4db19895e8b79ee7920f2bc9ead80230b96eb Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 7 Jul 2023 10:43:18 +0200 Subject: [PATCH 11/17] happy lint, happy life --- params/protocol_params.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/params/protocol_params.go b/params/protocol_params.go index b8444793cae6..ce2edfabc5a6 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -160,8 +160,8 @@ const ( RefundQuotient uint64 = 2 RefundQuotientEIP3529 uint64 = 5 - BeaconRootStorageAddress byte = 0x0B - HistoricalRootModulus = 98304 // Limits how many historical roots are stored by EIP-4788 + BeaconRootStorageAddress byte = 0x0B + HistoricalRootModulus uint64 = 98304 // Limits how many historical roots are stored by EIP-4788 BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob From 9a11599ab1e3f13f67f1f1fa274033c391e30a02 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 7 Jul 2023 13:57:36 +0200 Subject: [PATCH 12/17] core: make sure to only apply beaconroot after cancun! --- core/state_processor.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/state_processor.go b/core/state_processor.go index a30fc341430d..ed02725ab4bf 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -71,9 +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 header.BeaconRoot != nil { + 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) From 8a030c971071dd7cd2ad18c49abf7d32f0da2d28 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 25 Jul 2023 10:52:37 +0200 Subject: [PATCH 13/17] params, consensus: fix review comments --- consensus/misc/eip4788.go | 10 ++--- consensus/misc/eip4788_test.go | 67 ++++++++++++++++++++++++++++++++++ core/vm/contracts.go | 32 ++++++++-------- params/protocol_params.go | 12 ++++-- 4 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 consensus/misc/eip4788_test.go diff --git a/consensus/misc/eip4788.go b/consensus/misc/eip4788.go index 1f8d0ad07f78..1ad97f1ca146 100644 --- a/consensus/misc/eip4788.go +++ b/consensus/misc/eip4788.go @@ -23,23 +23,21 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var historicalStorageAddress = common.BytesToAddress([]byte{params.BeaconRootStorageAddress}) - // 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(historicalStorageAddress, timeKey, time) - state.SetState(historicalStorageAddress, rootKey, root) + state.SetState(params.BeaconRootsStorageAddress, timeKey, time) + state.SetState(params.BeaconRootsStorageAddress, rootKey, root) } func calcBeaconRootIndices(header *types.Header) (timeKey, time, rootKey, root common.Hash) { // timeKey -> header.Time - timeIndex := header.Time % params.HistoricalRootModulus + timeIndex := header.Time % params.HistoricalRootsModulus timeKey = common.Uint64ToHash(timeIndex) time = common.Uint64ToHash(header.Time) // rootKey -> header.BeaconRoot - rootKey = common.Uint64ToHash(timeIndex + params.HistoricalRootModulus) + 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..d3fa1b4d99cb --- /dev/null +++ b/consensus/misc/eip4788_test.go @@ -0,0 +1,67 @@ +// 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 ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +func TestCalcBeaconRootIndices(t *testing.T) { + tests := []struct { + Header types.Header + TimeKey common.Hash + Time common.Hash + RootKey common.Hash + Root common.Hash + }{ + { + Header: types.Header{Time: 0, BeaconRoot: &common.Hash{1}}, + TimeKey: common.Hash{}, + Time: common.Hash{}, + RootKey: common.BigToHash(big.NewInt(int64(params.HistoricalRootsModulus))), + Root: common.Hash{1}, + }, + { + Header: types.Header{Time: 120265298769267, BeaconRoot: &common.Hash{0xff, 0xfe, 0xfc}}, + TimeKey: common.BytesToHash([]byte{0xf5, 0x73}), // 120265298769267 % params.HistoricalRootsModulus + Time: common.BytesToHash([]byte{0x6D, 0x61, 0x72, 0x69, 0x75, 0x73}), // 120265298769267 -> hex + RootKey: common.BytesToHash([]byte{0x02, 0x75, 0x73}), // params.HistoricalRootsModulus + 0xf5 0x73 + Root: common.Hash{0xff, 0xfe, 0xfc}, + }, + } + + for _, test := range tests { + timeKey, time, rootKey, root := calcBeaconRootIndices(&test.Header) + if timeKey != test.TimeKey { + t.Fatalf("invalid time key: got %v want %v", timeKey, test.TimeKey) + } + if time != test.Time { + t.Fatalf("invalid time: got %v want %v", time, test.Time) + } + if rootKey != test.RootKey { + t.Fatalf("invalid time key: got %v want %v", rootKey, test.RootKey) + } + if root != test.Root { + t.Fatalf("invalid time key: got %v want %v", root, test.Root) + } + } +} diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 5f36b6890479..ed794941bca9 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -126,16 +126,16 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ } var PrecompiledContracts4788 = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, - common.BytesToAddress([]byte{params.BeaconRootStorageAddress}): &beaconRoot{}, + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, + common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, + params.BeaconRootsStorageAddress: &beaconRoot{}, } var ( @@ -1156,10 +1156,8 @@ func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash { // BeaconRoot is a stateful precompile that returns a beacon root. type beaconRoot struct{} -var beaconRootStorageAddress = common.BytesToAddress([]byte{params.BeaconRootStorageAddress}) - func (c *beaconRoot) RequiredGas(input []byte) uint64 { - return 4200 + return params.BeaconRootPrecompileGas } func (c *beaconRoot) Run(stateDB StateReader, input []byte) ([]byte, error) { @@ -1169,13 +1167,13 @@ func (c *beaconRoot) Run(stateDB StateReader, input []byte) ([]byte, error) { var timestamp common.Hash copy(timestamp[:], input) // retrieve stored timestamp - timeIndex := binary.BigEndian.Uint64(timestamp[24:]) % params.HistoricalRootModulus - recordedTimestamp := stateDB.GetState(beaconRootStorageAddress, common.Uint64ToHash(timeIndex)) + 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.HistoricalRootModulus - beaconRoot := stateDB.GetState(beaconRootStorageAddress, common.Uint64ToHash(rootIndex)) + rootIndex := timeIndex + params.HistoricalRootsModulus + beaconRoot := stateDB.GetState(params.BeaconRootsStorageAddress, common.Uint64ToHash(rootIndex)) return beaconRoot[:], nil } diff --git a/params/protocol_params.go b/params/protocol_params.go index ce2edfabc5a6..099ca48586da 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,8 +164,8 @@ const ( RefundQuotient uint64 = 2 RefundQuotientEIP3529 uint64 = 5 - BeaconRootStorageAddress byte = 0x0B - HistoricalRootModulus uint64 = 98304 // Limits how many historical roots are stored by EIP-4788 + 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 @@ -182,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 ) From 8057030ca0362d619084a2a1b9eb9e962c0f8533 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 27 Jul 2023 15:03:22 +0200 Subject: [PATCH 14/17] consensus/misc: turn 4788-beaconroot calc tests into json --- consensus/misc/eip4788_test.go | 64 +++++++++---------- .../misc/testdata/eip4788_beaconroot.json | 26 ++++++++ 2 files changed, 56 insertions(+), 34 deletions(-) create mode 100644 consensus/misc/testdata/eip4788_beaconroot.json diff --git a/consensus/misc/eip4788_test.go b/consensus/misc/eip4788_test.go index d3fa1b4d99cb..5b1f581993af 100644 --- a/consensus/misc/eip4788_test.go +++ b/consensus/misc/eip4788_test.go @@ -17,51 +17,47 @@ package misc import ( - "math/big" + "encoding/json" + "os" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" ) +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) { - tests := []struct { - Header types.Header - TimeKey common.Hash - Time common.Hash - RootKey common.Hash - Root common.Hash - }{ - { - Header: types.Header{Time: 0, BeaconRoot: &common.Hash{1}}, - TimeKey: common.Hash{}, - Time: common.Hash{}, - RootKey: common.BigToHash(big.NewInt(int64(params.HistoricalRootsModulus))), - Root: common.Hash{1}, - }, - { - Header: types.Header{Time: 120265298769267, BeaconRoot: &common.Hash{0xff, 0xfe, 0xfc}}, - TimeKey: common.BytesToHash([]byte{0xf5, 0x73}), // 120265298769267 % params.HistoricalRootsModulus - Time: common.BytesToHash([]byte{0x6D, 0x61, 0x72, 0x69, 0x75, 0x73}), // 120265298769267 -> hex - RootKey: common.BytesToHash([]byte{0x02, 0x75, 0x73}), // params.HistoricalRootsModulus + 0xf5 0x73 - Root: common.Hash{0xff, 0xfe, 0xfc}, - }, + data, err := os.ReadFile("./testdata/eip4788_beaconroot.json") + if err != nil { + t.Fatal(err) } - - for _, test := range tests { - timeKey, time, rootKey, root := calcBeaconRootIndices(&test.Header) - if timeKey != test.TimeKey { - t.Fatalf("invalid time key: got %v want %v", timeKey, test.TimeKey) + 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 time != test.Time { - t.Fatalf("invalid time: got %v want %v", time, test.Time) + if haveTime != tc.Time { + t.Errorf("test %d: invalid time: \nhave %v\nwant %v", i, haveTime, tc.Time) } - if rootKey != test.RootKey { - t.Fatalf("invalid time key: got %v want %v", rootKey, test.RootKey) + if haveRootKey != tc.RootKey { + t.Errorf("test %d: invalid root key: \nhave %v\nwant %v", i, haveRootKey, tc.RootKey) } - if root != test.Root { - t.Fatalf("invalid time key: got %v want %v", root, test.Root) + 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 From 2ce4c013b6c28b6ee9bee36e11de38e2a7b7536b Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 31 Jul 2023 19:30:38 +0200 Subject: [PATCH 15/17] consensus/misc: set beaconroot precompile non-zero nonce (poc) --- consensus/misc/eip4788.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/consensus/misc/eip4788.go b/consensus/misc/eip4788.go index 1ad97f1ca146..6a1fae0115bb 100644 --- a/consensus/misc/eip4788.go +++ b/consensus/misc/eip4788.go @@ -29,6 +29,10 @@ func ApplyBeaconRoot(header *types.Header, state *state.StateDB) { 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) { From b544bd64a9b02cdf31cffef740457feda065e9b9 Mon Sep 17 00:00:00 2001 From: lightclient Date: Mon, 31 Jul 2023 14:42:11 -0600 Subject: [PATCH 16/17] core/types: add beacon root getter for block --- core/types/block.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/types/block.go b/core/types/block.go index 7e8c4fea56bd..1ca822323337 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -91,7 +91,7 @@ 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 was added by EIP-4788 and is ignored in legacy headers. BeaconRoot *common.Hash `json:"beaconRoot" rlp:"optional"` } @@ -381,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. From ba8ea619ae1cae3e2a470fad7fa88af3b6317f25 Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Mon, 31 Jul 2023 14:14:48 +0100 Subject: [PATCH 17/17] core/vm: add beacon root pre-compile address to cancun contracts. --- core/vm/contracts.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index ed794941bca9..f3455c69d51e 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -109,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 @@ -125,19 +126,6 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{18}): &bls12381MapG2{}, } -var PrecompiledContracts4788 = map[common.Address]PrecompiledContract{ - common.BytesToAddress([]byte{1}): &ecrecover{}, - common.BytesToAddress([]byte{2}): &sha256hash{}, - common.BytesToAddress([]byte{3}): &ripemd160hash{}, - common.BytesToAddress([]byte{4}): &dataCopy{}, - common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true}, - common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, - params.BeaconRootsStorageAddress: &beaconRoot{}, -} - var ( PrecompiledAddressesCancun []common.Address PrecompiledAddressesBerlin []common.Address