diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 393129a3ac14..922d10648e5e 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -152,6 +152,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if chainConfig.CurieBlock != nil && chainConfig.CurieBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { misc.ApplyCurieHardFork(statedb) } + // Apply EIP-2935 + if pre.Env.BlockHashes != nil && chainConfig.IsFeynman(pre.Env.Timestamp) { + var ( + prevNumber = pre.Env.Number - 1 + prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)] + evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig) + ) + core.ProcessParentBlockHash(prevHash, evm, statedb) + } for i, tx := range txs { msg, err := tx.AsMessage(signer, pre.Env.BaseFee) diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index 1a1535a8757e..d7a8002f61d6 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -140,15 +140,29 @@ func Transaction(ctx *cli.Context) error { r.Address = sender } // Check intrinsic gas - if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, - chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int))); err != nil { + rules := chainConfig.Rules(common.Big0, 0) + gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) + if err != nil { r.Error = err results = append(results, r) continue - } else { - r.IntrinsicGas = gas - if tx.Gas() < gas { - r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas) + } + r.IntrinsicGas = gas + if tx.Gas() < gas { + r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas) + results = append(results, r) + continue + } + // For Feynman txs, validate the floor data gas. + if rules.IsFeynman { + floorDataGas, err := core.FloorDataGas(tx.Data()) + if err != nil { + r.Error = err + results = append(results, r) + continue + } + if tx.Gas() < floorDataGas { + r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrFloorDataGas, tx.Gas(), floorDataGas) results = append(results, r) continue } diff --git a/core/error.go b/core/error.go index 54ec1b188dba..98a637e95840 100644 --- a/core/error.go +++ b/core/error.go @@ -81,6 +81,10 @@ var ( // than required to start the invocation. ErrIntrinsicGas = errors.New("intrinsic gas too low") + // ErrFloorDataGas is returned if the transaction is specified to use less gas + // than required for the data floor cost. + ErrFloorDataGas = errors.New("insufficient gas for floor data gas cost") + // ErrTxTypeNotSupported is returned if a transaction is not supported in the // current network configuration. ErrTxTypeNotSupported = types.ErrTxTypeNotSupported diff --git a/core/genesis.go b/core/genesis.go index 9554be0ac304..a5eea6f30c3f 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -500,6 +500,8 @@ func DeveloperGenesisBlock(period uint64, gasLimit uint64, faucet common.Address common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b + // Pre-deploy EIP-2935 history contract. + params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0}, // LSH 250 due to finite field limitation faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 250), big.NewInt(9))}, }, diff --git a/core/state/statedb.go b/core/state/statedb.go index 94a89349afbb..5302263d363c 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -167,6 +167,11 @@ func (s *StateDB) WithWitness(witness *stateless.Witness) { s.witness = witness } +// Witness returns the current witness for the state database. +func (s *StateDB) Witness() *stateless.Witness { + return s.witness +} + // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the // state trie concurrently while the state is mutated so that when we reach the // commit phase, most of the needed data is already hot. diff --git a/core/state_processor.go b/core/state_processor.go index 7a6c5d0d9783..ddbc9d43bd43 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -94,6 +94,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg blockContext := NewEVMBlockContext(header, p.bc, p.config, nil) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) processorBlockTransactionGauge.Update(int64(block.Transactions().Len())) + // Apply EIP-2935 + if p.config.IsFeynman(block.Time()) { + ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) + } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number, header.Time), header.BaseFee) @@ -199,3 +203,30 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } + +// ProcessParentBlockHash stores the parent block hash in the history storage contract +// as per EIP-2935. +func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, statedb *state.StateDB) { + msg := types.NewMessage( + params.SystemAddress, // from + ¶ms.HistoryStorageAddress, // to + 0, // nonce + common.Big0, // amount + 30_000_000, // gasLimit + common.Big0, // gasPrice + common.Big0, // gasFeeCap + common.Big0, // gasTipCap + prevHash.Bytes(), // data + nil, // accessList + false, // isFake + nil, // setCodeAuthorizations + ) + + evm.Reset(NewEVMTxContext(msg), statedb) + statedb.AddAddressToAccessList(params.HistoryStorageAddress) + _, _, err := evm.Call(vm.AccountRef(msg.From()), *msg.To(), msg.Data(), 30_000_000, common.Big0, nil) + if err != nil { + panic(err) + } + statedb.Finalise(true) +} diff --git a/core/state_processor_test.go b/core/state_processor_test.go index a37e90e93c86..7fbc812cb6d2 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -18,6 +18,7 @@ package core import ( "crypto/ecdsa" + "encoding/binary" "math/big" "testing" @@ -31,9 +32,11 @@ import ( "github.com/scroll-tech/go-ethereum/consensus/ethash" "github.com/scroll-tech/go-ethereum/consensus/misc" "github.com/scroll-tech/go-ethereum/core/rawdb" + "github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/vm" "github.com/scroll-tech/go-ethereum/crypto" + "github.com/scroll-tech/go-ethereum/ethdb/memorydb" "github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/trie" ) @@ -64,6 +67,7 @@ func TestStateProcessorErrors(t *testing.T) { DarwinV2Time: new(uint64), EuclidTime: new(uint64), EuclidV2Time: new(uint64), + FeynmanTime: new(uint64), Ethash: new(params.EthashConfig), } signer = types.LatestSigner(config) @@ -387,9 +391,9 @@ func TestStateProcessorErrors(t *testing.T) { }{ { // ErrMaxInitCodeSizeExceeded txs: []*types.Transaction{ - mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), tooBigInitCode[:]), + mkDynamicCreationTx(0, 520000, common.Big0, misc.CalcBaseFee(config, genesis.Header(), parentL1BaseFee), tooBigInitCode[:]), }, - want: "could not apply tx 0 [0x7b33776d375660694a23ef992c090265682f3687607e0099b14503fdb65d73e3]: max initcode size exceeded: code size 49153 limit 49152", + want: "could not apply tx 0 [0xe0d03426cecc04467410064cb4de02012fc069d2462282735d7dfcb9dea9f63b]: max initcode size exceeded: code size 49153 limit 49152", }, { // ErrIntrinsicGas: Not enough gas to cover init code txs: []*types.Transaction{ @@ -453,3 +457,68 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr // Assemble and return the final block for sealing return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)) } + +func TestProcessParentBlockHash(t *testing.T) { + var ( + chainConfig = ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ShanghaiBlock: big.NewInt(0), + BernoulliBlock: big.NewInt(0), + CurieBlock: big.NewInt(0), + DarwinTime: new(uint64), + DarwinV2Time: new(uint64), + EuclidTime: new(uint64), + EuclidV2Time: new(uint64), + FeynmanTime: new(uint64), + Ethash: new(params.EthashConfig), + } + hashA = common.Hash{0x01} + hashB = common.Hash{0x02} + header = &types.Header{ParentHash: hashA, Number: big.NewInt(2), Difficulty: big.NewInt(0)} + parent = &types.Header{ParentHash: hashB, Number: big.NewInt(1), Difficulty: big.NewInt(0)} + coinbase = common.Address{} + ) + test := func(statedb *state.StateDB) { + statedb.SetNonce(params.HistoryStorageAddress, 1) + statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode) + statedb.IntermediateRoot(true) + + vmContext := NewEVMBlockContext(header, nil, chainConfig, &coinbase) + evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{}) + ProcessParentBlockHash(header.ParentHash, evm, statedb) + + vmContext = NewEVMBlockContext(parent, nil, chainConfig, &coinbase) + evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{}) + ProcessParentBlockHash(parent.ParentHash, evm, statedb) + + // make sure that the state is correct + if have := getParentBlockHash(statedb, 1); have != hashA { + t.Errorf("want parent hash %v, have %v", hashA, have) + } + if have := getParentBlockHash(statedb, 0); have != hashB { + t.Errorf("want parent hash %v, have %v", hashB, have) + } + } + t.Run("MPT", func(t *testing.T) { + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) + test(statedb) + }) +} + +func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash { + ringIndex := number % params.HistoryServeWindow + var key common.Hash + binary.BigEndian.PutUint64(key[24:], ringIndex) + return statedb.GetState(params.HistoryStorageAddress, key) +} diff --git a/core/state_transition.go b/core/state_transition.go index cc75301f1456..0b0194592752 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -17,6 +17,7 @@ package core import ( + "bytes" "fmt" "math" "math/big" @@ -141,12 +142,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set // Bump the required gas by the amount of transactional data if dataLen > 0 { // Zero and non-zero bytes are priced differently - var nz uint64 - for _, byt := range data { - if byt != 0 { - nz++ - } - } + z := uint64(bytes.Count(data, []byte{0})) + nz := dataLen - z + // Make sure we don't exceed uint64 for all data combinations nonZeroGas := params.TxDataNonZeroGasFrontier if isEIP2028 { @@ -157,7 +155,6 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set } gas += nz * nonZeroGas - z := dataLen - nz if (math.MaxUint64-gas)/params.TxDataZeroGas < z { return 0, ErrGasUintOverflow } @@ -181,6 +178,21 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set return gas, nil } +// FloorDataGas computes the minimum gas required for a transaction based on its data tokens (EIP-7623). +func FloorDataGas(data []byte) (uint64, error) { + var ( + z = uint64(bytes.Count(data, []byte{0})) + nz = uint64(len(data)) - z + tokens = nz*params.TxTokenPerNonZeroByte + z + ) + // Check for overflow + if (math.MaxUint64-params.TxGas)/params.TxCostFloorPerToken < tokens { + return 0, ErrGasUintOverflow + } + // Minimum gas required for a transaction based on its data tokens (EIP-7623). + return params.TxGas + tokens*params.TxCostFloorPerToken, nil +} + // toWordSize returns the ceiled word size required for init code payment calculation. func toWordSize(size uint64) uint64 { if size > math.MaxUint64-31 { @@ -380,16 +392,30 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { sender = vm.AccountRef(msg.From()) rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Time.Uint64()) contractCreation = msg.To() == nil + floorDataGas uint64 ) // Check clauses 4-5, subtract intrinsic gas if everything is correct gas, err := IntrinsicGas(st.data, st.msg.AccessList(), st.msg.SetCodeAuthorizations(), contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) if err != nil { + // Note: The L1 message queue contract ensures that this cannot happen for L1 messages. return nil, err } if st.gas < gas { + // Note: The L1 message queue contract ensures that this cannot happen for L1 messages. return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas) } + // Gas limit suffices for the floor data cost (EIP-7623) + if rules.IsFeynman { + floorDataGas, err = FloorDataGas(st.data) + if err != nil { + return nil, err + } + if st.gas < floorDataGas { + // Note: The L1 message queue contract ensures that this cannot happen for L1 messages. + return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, st.gas, floorDataGas) + } + } st.gas -= gas // Check clause 6 @@ -455,13 +481,16 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { }, nil } - if !rules.IsLondon { - // Before EIP-3529: refunds were capped to gasUsed / 2 - st.refundGas(params.RefundQuotient) - } else { - // After EIP-3529: refunds are capped to gasUsed / 5 - st.refundGas(params.RefundQuotientEIP3529) + // Compute refund counter, capped to a refund quotient. + st.gas += st.calcRefund() + if rules.IsFeynman { + // After EIP-7623: Data-heavy transactions pay the floor gas. + if st.gasUsed() < floorDataGas { + st.gas = st.initialGas - floorDataGas + } } + st.returnGas() + effectiveTip := st.gasPrice // only burn the base fee if the fee vault is not enabled @@ -545,14 +574,25 @@ func (st *StateTransition) applyAuthorization(auth *types.SetCodeAuthorization) return types.AuthorizationResult{Authority: authority, PreCode: preCode, Success: true} } -func (st *StateTransition) refundGas(refundQuotient uint64) { - // Apply refund counter, capped to a refund quotient - refund := st.gasUsed() / refundQuotient +// calcRefund computes refund counter, capped to a refund quotient. +func (st *StateTransition) calcRefund() uint64 { + var refund uint64 + if !st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { + // Before EIP-3529: refunds were capped to gasUsed / 2 + refund = st.gasUsed() / params.RefundQuotient + } else { + // After EIP-3529: refunds are capped to gasUsed / 5 + refund = st.gasUsed() / params.RefundQuotientEIP3529 + } if refund > st.state.GetRefund() { refund = st.state.GetRefund() } - st.gas += refund + return refund +} +// returnGas returns ETH for remaining gas, +// exchanged at the original rate. +func (st *StateTransition) returnGas() { // Return ETH for remaining gas, exchanged at the original rate. remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) st.state.AddBalance(st.msg.From(), remaining) diff --git a/core/tx_pool.go b/core/tx_pool.go index 67f3ee101224..5a4afcb1a4f8 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -297,6 +297,7 @@ type TxPool struct { eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions. shanghai bool // Fork indicator whether we are in the Shanghai stage. eip7702 bool // Fork indicator whether we are using EIP-7702 type transactions. + feynman bool // Fork indicator whether we are in the Feynman stage. currentState *state.StateDB // Current state in the blockchain head currentHead *big.Int // Current blockchain head @@ -855,6 +856,16 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if tx.Gas() < intrGas { return ErrIntrinsicGas } + // Ensure the transaction can cover floor data gas. + if pool.feynman { + floorDataGas, err := FloorDataGas(tx.Data()) + if err != nil { + return err + } + if tx.Gas() < floorDataGas { + return fmt.Errorf("%w: gas %v, minimum needed %v", ErrFloorDataGas, tx.Gas(), floorDataGas) + } + } return nil } @@ -1583,6 +1594,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { pool.eip1559 = pool.chainconfig.IsCurie(next) pool.shanghai = pool.chainconfig.IsShanghai(next) pool.eip7702 = pool.chainconfig.IsEuclidV2(newHead.Time) + pool.feynman = pool.chainconfig.IsFeynman(newHead.Time) // Update current head pool.currentHead = next diff --git a/core/vm/contracts.go b/core/vm/contracts.go index d115182d49b3..97b22977006d 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -80,7 +80,7 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false}, common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{limitInputLength: false}, common.BytesToAddress([]byte{9}): &blake2F{}, } @@ -94,7 +94,7 @@ var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{ 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{8}): &bn256PairingIstanbul{limitInputLength: false}, common.BytesToAddress([]byte{9}): &blake2F{}, } @@ -108,7 +108,7 @@ var PrecompiledContractsArchimedes = map[common.Address]PrecompiledContract{ 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{8}): &bn256PairingIstanbul{limitInputLength: true}, common.BytesToAddress([]byte{9}): &blake2FDisabled{}, } @@ -122,7 +122,7 @@ var PrecompiledContractsBernoulli = map[common.Address]PrecompiledContract{ 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{8}): &bn256PairingIstanbul{limitInputLength: true}, common.BytesToAddress([]byte{9}): &blake2FDisabled{}, } @@ -136,7 +136,22 @@ var PrecompiledContractsEuclidV2 = map[common.Address]PrecompiledContract{ 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{8}): &bn256PairingIstanbul{limitInputLength: true}, + common.BytesToAddress([]byte{9}): &blake2FDisabled{}, + common.BytesToAddress([]byte{0x01, 0x00}): &p256Verify{}, +} + +// PrecompiledContractsFeynman contains the default set of pre-compiled Ethereum +// contracts used in the Feynman release. +var PrecompiledContractsFeynman = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hashDisabled{}, + 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{limitInputLength: false}, common.BytesToAddress([]byte{9}): &blake2FDisabled{}, common.BytesToAddress([]byte{0x01, 0x00}): &p256Verify{}, } @@ -156,6 +171,7 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ } var ( + PrecompiledAddressesFeynman []common.Address PrecompiledAddressesEuclidV2 []common.Address PrecompiledAddressesBernoulli []common.Address PrecompiledAddressesArchimedes []common.Address @@ -187,11 +203,16 @@ func init() { for k := range PrecompiledContractsEuclidV2 { PrecompiledAddressesEuclidV2 = append(PrecompiledAddressesEuclidV2, k) } + for k := range PrecompiledContractsFeynman { + PrecompiledAddressesFeynman = append(PrecompiledAddressesFeynman, k) + } } // ActivePrecompiles returns the precompiles enabled with the current configuration. func ActivePrecompiles(rules params.Rules) []common.Address { switch { + case rules.IsFeynman: + return PrecompiledAddressesFeynman case rules.IsEuclidV2: return PrecompiledAddressesEuclidV2 case rules.IsBernoulli: @@ -600,10 +621,6 @@ var ( // runBn256Pairing implements the Bn256Pairing precompile, referenced by both // Byzantium and Istanbul operations. func runBn256Pairing(input []byte) ([]byte, error) { - // Allow at most 4 inputs - if len(input) > 4*192 { - return nil, errBadPairingInput - } // Handle some corner cases cheaply if len(input)%192 > 0 { return nil, errBadPairingInput @@ -634,7 +651,9 @@ func runBn256Pairing(input []byte) ([]byte, error) { // bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve // conforming to Istanbul consensus rules. -type bn256PairingIstanbul struct{} +type bn256PairingIstanbul struct { + limitInputLength bool +} // RequiredGas returns the gas required to execute the pre-compiled contract. func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { @@ -642,6 +661,13 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 { } func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) { + if c.limitInputLength { + // Allow at most 4 inputs + if len(input) > 4*192 { + return nil, errBadPairingInput + } + } + return runBn256Pairing(input) } diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index e028d45f6511..f6217b0ffd5b 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -65,6 +65,7 @@ var allPrecompiles = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{16}): &bls12381Pairing{}, common.BytesToAddress([]byte{17}): &bls12381MapG1{}, common.BytesToAddress([]byte{18}): &bls12381MapG2{}, + common.BytesToAddress([]byte{0xf8}): &bn256PairingIstanbul{limitInputLength: true}, common.BytesToAddress([]byte{0x01, 0x00}): &p256Verify{}, } @@ -264,7 +265,7 @@ func BenchmarkPrecompiledBn256ScalarMul(b *testing.B) { benchJson("bn256ScalarMu // Tests the sample inputs from the elliptic curve pairing check EIP 197. func TestPrecompiledBn256Pairing(t *testing.T) { testJson("bn256Pairing", "08", t) } -func TestPrecompiledBn256PairingFail(t *testing.T) { testJsonFail("bn256Pairing", "08", t) } +func TestPrecompiledBn256PairingFail(t *testing.T) { testJsonFail("bn256Pairing", "f8", t) } func BenchmarkPrecompiledBn256Pairing(b *testing.B) { benchJson("bn256Pairing", "08", b) } func TestPrecompiledBlake2F(t *testing.T) { testJson("blake2F", "09", t) } diff --git a/core/vm/evm.go b/core/vm/evm.go index afb5c7cb39be..0514677c611a 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -47,6 +47,8 @@ type ( func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { var precompiles map[common.Address]PrecompiledContract switch { + case evm.chainRules.IsFeynman: + precompiles = PrecompiledContractsFeynman case evm.chainRules.IsEuclidV2: precompiles = PrecompiledContractsEuclidV2 case evm.chainRules.IsBernoulli: diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 36a467a8f3ff..8027afae764d 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -470,6 +470,32 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( return nil, nil } +func opBlockhashPostFeynman(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + num := scope.Stack.peek() + num64, overflow := num.Uint64WithOverflow() + if overflow { + num.Clear() + return nil, nil + } + var upper, lower uint64 + upper = interpreter.evm.Context.BlockNumber.Uint64() + if upper < 257 { + lower = 0 + } else { + lower = upper - 256 + } + if num64 >= lower && num64 < upper { + res := interpreter.evm.Context.GetHash(num64) + if witness := interpreter.evm.StateDB.Witness(); witness != nil { + witness.AddBlockHash(num64) + } + num.SetBytes(res[:]) + } else { + num.Clear() + } + return nil, nil +} + func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.FeeRecipient().Bytes())) return nil, nil diff --git a/core/vm/interface.go b/core/vm/interface.go index fe27905e9116..2d19515c3712 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/core/stateless" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/params" ) @@ -85,6 +86,8 @@ type StateDB interface { AddPreimage(common.Hash, []byte) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error + + Witness() *stateless.Witness } // CallContext provides a basic interface for the EVM calling conventions. The EVM diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 81de5f3c0577..28b5972e7d8f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -74,6 +74,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { if cfg.JumpTable[STOP] == nil { var jt JumpTable switch { + case evm.chainRules.IsFeynman: + jt = feynmanInstructionSet case evm.chainRules.IsEuclidV2: jt = euclidV2InstructionSet case evm.chainRules.IsDarwin: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 5c46758dbf7d..92a1a8c7900b 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -62,11 +62,24 @@ var ( curieInstructionSet = newCurieInstructionSet() darwinInstructionSet = newDarwinInstructionSet() euclidV2InstructionSet = newEuclidV2InstructionSet() + feynmanInstructionSet = newFeynmanInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +// newFeynmanInstructionSet returns the frontier, homestead, byzantium, +// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin, euclidV2, +// and feynman instructions. +func newFeynmanInstructionSet() JumpTable { + instructionSet := newEuclidV2InstructionSet() + + // change block hash opcode implementation + instructionSet[BLOCKHASH].execute = opBlockhashPostFeynman + + return instructionSet +} + // newEuclidV2InstructionSet returns the frontier, homestead, byzantium, // contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin and euclidV2 instructions. func newEuclidV2InstructionSet() JumpTable { diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 7e4029aec419..2d21fd37c148 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -74,6 +74,8 @@ func setDefaults(cfg *Config) { CurieBlock: new(big.Int), DarwinTime: new(uint64), DarwinV2Time: new(uint64), + EuclidV2Time: new(uint64), + FeynmanTime: new(uint64), } } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index fa1556fa59cb..a1b5a9d5b275 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -337,17 +337,13 @@ func TestBlockhash(t *testing.T) { if zero.BitLen() != 0 { t.Fatalf("expected zeroes, got %x", ret[0:32]) } - firstExpectedHash := new(big.Int) - firstExpectedHash.SetString("13215081625009140218242111988507489764601005198286886925088730931502473149599", 10) - if first.Uint64() != firstExpectedHash.Uint64() { - t.Fatalf("first hash should be 13215081625009140218242111988507489764601005198286886925088730931502473149599, got %d (%x)", first, ret[32:64]) + if first.Uint64() != 999 { + t.Fatalf("second block should be 999, got %d (%x)", first, ret[32:64]) } - lastExpectedHash := new(big.Int) - lastExpectedHash.SetString("2851160567348483005169712516804956111111231427377973738952179767509712807467", 10) - if last.Uint64() != lastExpectedHash.Uint64() { - t.Fatalf("last hash should be 2851160567348483005169712516804956111111231427377973738952179767509712807467, got %d (%x)", last, ret[64:96]) + if last.Uint64() != 744 { + t.Fatalf("last block should be 744, got %d (%x)", last, ret[64:96]) } - if exp, got := 0, chain.counter; exp != got { + if exp, got := 255, chain.counter; exp != got { t.Errorf("suboptimal; too much chain iteration, expected %d, got %d", exp, got) } } diff --git a/core/vm/testdata/precompiles/bn256Pairing.json b/core/vm/testdata/precompiles/bn256Pairing.json index bd06969aa8f1..3fbed6b87cef 100644 --- a/core/vm/testdata/precompiles/bn256Pairing.json +++ b/core/vm/testdata/precompiles/bn256Pairing.json @@ -76,6 +76,20 @@ "Gas": 113000, "NoBenchmark": false }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "ten_point_match_1", + "Gas": 385000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "ten_point_match_2", + "Gas": 385000, + "NoBenchmark": false + }, { "Input": "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", "Expected": "0000000000000000000000000000000000000000000000000000000000000001", diff --git a/eth/state_accessor.go b/eth/state_accessor.go index c610edd85d79..13b80ded0375 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -176,6 +176,12 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec if err != nil { return nil, vm.BlockContext{}, nil, err } + // If feynman hardfork, insert parent block hash in the state as per EIP-2935. + if eth.blockchain.Config().IsFeynman(block.Time()) { + context := core.NewEVMBlockContext(block.Header(), eth.blockchain, eth.blockchain.Config(), nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, eth.blockchain.Config(), vm.Config{}) + core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) + } if txIndex == 0 && len(block.Transactions()) == 0 { return nil, vm.BlockContext{}, statedb, nil } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 271ddf963201..7b66326c0970 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -277,6 +277,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config for task := range tasks { signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), task.block.Time()) blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), api.backend.ChainConfig(), nil) + // EIP-2935: Insert parent hash in history contract. + if api.backend.ChainConfig().IsFeynman(task.block.Time()) { + evm := vm.NewEVM(blockCtx, vm.TxContext{}, task.statedb, api.backend.ChainConfig(), vm.Config{}) + core.ProcessParentBlockHash(task.block.ParentHash(), evm, task.statedb) + } // Trace all the transactions contained within for i, tx := range task.block.Transactions() { msg, _ := tx.AsMessage(signer, task.block.BaseFee()) @@ -539,6 +544,11 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), api.backend.ChainConfig(), nil) deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) ) + // EIP-2935: Insert parent hash in history contract. + if api.backend.ChainConfig().IsFeynman(block.Time()) { + vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{}) + core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) + } for i, tx := range block.Transactions() { var ( msg, _ = tx.AsMessage(signer, block.BaseFee()) @@ -614,6 +624,11 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac threads = len(txs) } blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), api.backend.ChainConfig(), nil) + // EIP-2935: Insert parent hash in history contract. + if api.backend.ChainConfig().IsFeynman(block.Time()) { + evm := vm.NewEVM(blockCtx, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) + core.ProcessParentBlockHash(block.ParentHash(), evm, statedb) + } blockHash := block.Hash() blockNumber := block.NumberU64() for th := 0; th < threads; th++ { @@ -748,6 +763,13 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block canon = false } } + + // EIP-2935: Insert parent hash in history contract. + if api.backend.ChainConfig().IsFeynman(block.Time()) { + evm := vm.NewEVM(vmctx, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) + core.ProcessParentBlockHash(block.ParentHash(), evm, statedb) + } + for i, tx := range block.Transactions() { // Prepare the trasaction for un-traced execution var ( diff --git a/miner/scroll_worker.go b/miner/scroll_worker.go index e58c6360e7b4..8af97f1cd7a9 100644 --- a/miner/scroll_worker.go +++ b/miner/scroll_worker.go @@ -627,15 +627,25 @@ func (w *worker) tryCommitNewWork(now time.Time, parent common.Hash, reorging bo // handleForks func (w *worker) handleForks() (bool, error) { + // Apply Curie predeployed contract update if w.chainConfig.CurieBlock != nil && w.chainConfig.CurieBlock.Cmp(w.current.header.Number) == 0 { misc.ApplyCurieHardFork(w.current.state) return true, nil } + // Stop/start miner at Euclid fork boundary on zktrie/mpt nodes if w.chainConfig.IsEuclid(w.current.header.Time) { parent := w.chain.GetBlockByHash(w.current.header.ParentHash) return parent != nil && !w.chainConfig.IsEuclid(parent.Time()), nil } + + // Apply EIP-2935 + if w.chainConfig.IsFeynman(w.current.header.Time) { + context := core.NewEVMBlockContext(w.current.header, w.chain, w.chainConfig, nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, w.current.state, w.chainConfig, vm.Config{}) + core.ProcessParentBlockHash(w.current.header.ParentHash, vmenv, w.current.state) + } + return false, nil } diff --git a/miner/scroll_worker_test.go b/miner/scroll_worker_test.go index cbbf82f32a59..74f1371c916d 100644 --- a/miner/scroll_worker_test.go +++ b/miner/scroll_worker_test.go @@ -1404,6 +1404,7 @@ func TestEuclidV2TransitionVerification(t *testing.T) { chainConfig := params.AllCliqueProtocolChanges.Clone() chainConfig.EuclidTime = newUint64(0) chainConfig.EuclidV2Time = newUint64(10000) + chainConfig.FeynmanTime = nil chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000} chainConfig.SystemContract = ¶ms.SystemContractConfig{Period: 1} diff --git a/params/config.go b/params/config.go index 96b77e04a7f4..eda4340e80c4 100644 --- a/params/config.go +++ b/params/config.go @@ -330,6 +330,7 @@ var ( DarwinV2Time: newUint64(1724832000), EuclidTime: newUint64(1741680000), EuclidV2Time: newUint64(1741852800), + FeynmanTime: nil, Clique: &CliqueConfig{ Period: 3, Epoch: 30000, @@ -381,6 +382,7 @@ var ( DarwinV2Time: newUint64(1725264000), EuclidTime: newUint64(1744815600), EuclidV2Time: newUint64(1745305200), + FeynmanTime: nil, Clique: &CliqueConfig{ Period: 3, Epoch: 30000, @@ -521,6 +523,7 @@ var ( DarwinV2Time: new(uint64), EuclidTime: new(uint64), EuclidV2Time: new(uint64), + FeynmanTime: new(uint64), TerminalTotalDifficulty: nil, Ethash: new(EthashConfig), Clique: nil, @@ -662,6 +665,7 @@ type ChainConfig struct { DarwinV2Time *uint64 `json:"darwinv2Time,omitempty"` // DarwinV2 switch time (nil = no fork, 0 = already on darwinv2) EuclidTime *uint64 `json:"euclidTime,omitempty"` // Euclid switch time (nil = no fork, 0 = already on euclid) EuclidV2Time *uint64 `json:"euclidv2Time,omitempty"` // EuclidV2 switch time (nil = no fork, 0 = already on euclidv2) + FeynmanTime *uint64 `json:"feynmanTime,omitempty"` // Feynman switch time (nil = no fork, 0 = already on feynman) // TerminalTotalDifficulty is the amount of total difficulty reached by // the network that triggers the consensus upgrade. @@ -852,7 +856,11 @@ func (c *ChainConfig) String() string { if c.EuclidV2Time != nil { euclidV2Time = fmt.Sprintf("@%v", *c.EuclidV2Time) } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, Archimedes: %v, Shanghai: %v, Bernoulli: %v, Curie: %v, Darwin: %v, DarwinV2: %v, Euclid: %v, EuclidV2: %v, Engine: %v, Scroll config: %v}", + feynmanTime := "" + if c.FeynmanTime != nil { + feynmanTime = fmt.Sprintf("@%v", *c.FeynmanTime) + } + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, Archimedes: %v, Shanghai: %v, Bernoulli: %v, Curie: %v, Darwin: %v, DarwinV2: %v, Euclid: %v, EuclidV2: %v, Feynman: %v, Engine: %v, Scroll config: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -876,6 +884,7 @@ func (c *ChainConfig) String() string { darwinV2Time, euclidTime, euclidV2Time, + feynmanTime, engine, c.Scroll, ) @@ -988,6 +997,11 @@ func (c *ChainConfig) IsEuclidV2(now uint64) bool { return isForkedTime(now, c.EuclidV2Time) } +// IsFeynman returns whether time is either equal to the Feynman fork time or greater. +func (c *ChainConfig) IsFeynman(now uint64) bool { + return isForkedTime(now, c.FeynmanTime) +} + // IsTerminalPoWBlock returns whether the given block is the last block of PoW stage. func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool { if c.TerminalTotalDifficulty == nil { @@ -1201,6 +1215,7 @@ type Rules struct { IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon, IsArchimedes, IsShanghai bool IsBernoulli, IsCurie, IsDarwin, IsEuclid, IsEuclidV2 bool + IsFeynman bool } // Rules ensures c's ChainID is not nil. @@ -1228,5 +1243,6 @@ func (c *ChainConfig) Rules(num *big.Int, time uint64) Rules { IsDarwin: c.IsDarwin(time), IsEuclid: c.IsEuclid(time), IsEuclidV2: c.IsEuclidV2(time), + IsFeynman: c.IsFeynman(time), } } diff --git a/params/protocol_params.go b/params/protocol_params.go index 7aab650058c6..7fa848db8305 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/scroll-tech/go-ethereum/common" +) const ( GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations. @@ -87,6 +91,8 @@ const ( TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) + TxTokenPerNonZeroByte uint64 = 4 // Token cost per non-zero byte as specified by EIP-7623. + TxCostFloorPerToken uint64 = 10 // Cost floor per byte of data as specified by EIP-7623. TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702 @@ -168,14 +174,27 @@ const ( BlobTxBlobGaspriceUpdateFraction = 5007716 // Controls the maximum rate of change for blob gas price, using Prague parameters BlobTxTargetBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Target consumable blob gas for data blobs per block (for 1559-like pricing), using Prague parameters + + HistoryServeWindow = 8192 // Number of blocks to serve historical block hashes for, EIP-2935. ) // Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations var Bls12381MultiExpDiscountTable = [128]uint64{1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, 408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, 281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222, 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 176, 176, 175, 174} +// Difficulty parameters. var ( DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. 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. ) + +// System contracts. +var ( + // SystemAddress is where the system-transaction is sent from as per EIP-4788 + SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") + + // EIP-2935 - Serve historical block hashes from state + HistoryStorageAddress = common.HexToAddress("0x0000F90827F1C53a10cb7A02335B175320002935") + HistoryStorageCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500") +) diff --git a/params/version.go b/params/version.go index 8f1068525298..4e4e9f19f35e 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 8 // Minor version component of the current release - VersionPatch = 53 // Patch version component of the current release + VersionPatch = 54 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string )