mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-01 04:28:37 +00:00
resolve merge conflict
This commit is contained in:
commit
729249ae48
34 changed files with 997 additions and 257 deletions
|
|
@ -14,8 +14,8 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build (darwin && !ios && cgo) || freebsd || (linux && !arm64) || netbsd || solaris
|
//go:build (darwin && !ios && cgo) || freebsd || linux || netbsd || solaris
|
||||||
// +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris
|
// +build darwin,!ios,cgo freebsd linux netbsd solaris
|
||||||
|
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build (darwin && !cgo) || ios || (linux && arm64) || windows || (!darwin && !freebsd && !linux && !netbsd && !solaris)
|
//go:build (darwin && !cgo) || ios || windows || (!darwin && !freebsd && !linux && !netbsd && !solaris)
|
||||||
// +build darwin,!cgo ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
// +build darwin,!cgo ios windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
||||||
|
|
||||||
// This is the fallback implementation of directory watching.
|
// This is the fallback implementation of directory watching.
|
||||||
// It is used on unsupported platforms.
|
// It is used on unsupported platforms.
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ func (hub *Hub) readPairings() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *Hub) writePairings() error {
|
func (hub *Hub) writePairings() error {
|
||||||
pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE, 0755)
|
pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -129,11 +129,8 @@ func (hub *Hub) writePairings() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := pairingFile.Write(pairingData); err != nil {
|
_, err = pairingFile.Write(pairingData)
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
|
func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@
|
||||||
package t8ntool
|
package t8ntool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
stdmath "math"
|
stdmath "math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
|
@ -47,6 +49,9 @@ type Prestate struct {
|
||||||
Env stEnv `json:"env"`
|
Env stEnv `json:"env"`
|
||||||
Pre types.GenesisAlloc `json:"pre"`
|
Pre types.GenesisAlloc `json:"pre"`
|
||||||
TreeLeaves map[common.Hash]hexutil.Bytes `json:"vkt,omitempty"`
|
TreeLeaves map[common.Hash]hexutil.Bytes `json:"vkt,omitempty"`
|
||||||
|
// AllocPath, when non-empty, causes Apply to stream the alloc from disk
|
||||||
|
// instead of reading Pre, so the full map never materializes in memory.
|
||||||
|
AllocPath string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type ExecutionResult -field-override executionResultMarshaling -out gen_execresult.go
|
//go:generate go run github.com/fjl/gencodec -type ExecutionResult -field-override executionResultMarshaling -out gen_execresult.go
|
||||||
|
|
@ -146,8 +151,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
isEIP4762 = chainConfig.IsUBT(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
|
isEIP4762 = chainConfig.IsUBT(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
|
||||||
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762)
|
statedb *state.StateDB
|
||||||
|
)
|
||||||
|
if pre.AllocPath != "" {
|
||||||
|
var err error
|
||||||
|
statedb, err = MakePreStateStreaming(rawdb.NewMemoryDatabase(), pre.AllocPath, isEIP4762)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762)
|
||||||
|
}
|
||||||
|
var (
|
||||||
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
|
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
|
||||||
gaspool = core.NewGasPool(pre.Env.GasLimit)
|
gaspool = core.NewGasPool(pre.Env.GasLimit)
|
||||||
blockHash = common.Hash{0x13, 0x37}
|
blockHash = common.Hash{0x13, 0x37}
|
||||||
|
|
@ -414,6 +430,76 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool
|
||||||
return statedb
|
return statedb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakePreStateStreaming is like MakePreState, but decodes the alloc from disk
|
||||||
|
// one account at a time so the full map is never held in memory.
|
||||||
|
func MakePreStateStreaming(db ethdb.Database, allocPath string, isBintrie bool) (*state.StateDB, error) {
|
||||||
|
tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true, IsUBT: isBintrie})
|
||||||
|
sdb := state.NewDatabase(tdb, nil)
|
||||||
|
|
||||||
|
root := types.EmptyRootHash
|
||||||
|
if isBintrie {
|
||||||
|
root = types.EmptyBinaryHash
|
||||||
|
}
|
||||||
|
statedb, err := state.New(root, sdb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorEVM, fmt.Errorf("failed to create initial statedb: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(allocPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err))
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
dec := json.NewDecoder(f)
|
||||||
|
tok, err := dec.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorJson, fmt.Errorf("failed reading alloc opening token: %v", err))
|
||||||
|
}
|
||||||
|
if d, ok := tok.(json.Delim); !ok || d != '{' {
|
||||||
|
return nil, NewError(ErrorJson, fmt.Errorf("expected alloc object, got %v", tok))
|
||||||
|
}
|
||||||
|
for dec.More() {
|
||||||
|
keyTok, err := dec.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorJson, fmt.Errorf("failed reading alloc key: %v", err))
|
||||||
|
}
|
||||||
|
keyStr, ok := keyTok.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, NewError(ErrorJson, fmt.Errorf("alloc key not a string: %v", keyTok))
|
||||||
|
}
|
||||||
|
addr := common.HexToAddress(keyStr)
|
||||||
|
var acct types.Account
|
||||||
|
if err := dec.Decode(&acct); err != nil {
|
||||||
|
return nil, NewError(ErrorJson, fmt.Errorf("failed decoding account %s: %v", keyStr, err))
|
||||||
|
}
|
||||||
|
statedb.SetCode(addr, acct.Code, tracing.CodeChangeUnspecified)
|
||||||
|
statedb.SetNonce(addr, acct.Nonce, tracing.NonceChangeGenesis)
|
||||||
|
if acct.Balance != nil {
|
||||||
|
statedb.SetBalance(addr, uint256.MustFromBig(acct.Balance), tracing.BalanceIncreaseGenesisBalance)
|
||||||
|
}
|
||||||
|
for k, v := range acct.Storage {
|
||||||
|
statedb.SetState(addr, k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := dec.Token(); err != nil {
|
||||||
|
return nil, NewError(ErrorJson, fmt.Errorf("failed reading alloc closing token: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
root, err = statedb.Commit(0, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorEVM, fmt.Errorf("failed to commit initial state: %v", err))
|
||||||
|
}
|
||||||
|
if isBintrie {
|
||||||
|
return statedb, nil
|
||||||
|
}
|
||||||
|
statedb, err = state.New(root, sdb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewError(ErrorEVM, fmt.Errorf("failed to reopen state after commit: %v", err))
|
||||||
|
}
|
||||||
|
return statedb, nil
|
||||||
|
}
|
||||||
|
|
||||||
func rlpHash(x any) (h common.Hash) {
|
func rlpHash(x any) (h common.Hash) {
|
||||||
hw := keccak.NewLegacyKeccak256()
|
hw := keccak.NewLegacyKeccak256()
|
||||||
rlp.Encode(hw, x)
|
rlp.Encode(hw, x)
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,9 @@ func Transaction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if chainConfig.IsOsaka(new(big.Int), 0) && tx.Gas() > params.MaxTxGas {
|
isOsaka := chainConfig.IsOsaka(new(big.Int), 0)
|
||||||
|
isAmsterdam := chainConfig.IsAmsterdam(new(big.Int), 0)
|
||||||
|
if isOsaka && !isAmsterdam && tx.Gas() > params.MaxTxGas {
|
||||||
r.Error = errors.New("gas limit exceeds maximum")
|
r.Error = errors.New("gas limit exceeds maximum")
|
||||||
}
|
}
|
||||||
results = append(results, r)
|
results = append(results, r)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package t8ntool
|
package t8ntool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -115,11 +116,10 @@ func Transition(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if allocStr != stdinSelector {
|
if allocStr != stdinSelector {
|
||||||
if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil {
|
prestate.AllocPath = allocStr
|
||||||
return err
|
} else {
|
||||||
}
|
prestate.Pre = inputData.Alloc
|
||||||
}
|
}
|
||||||
prestate.Pre = inputData.Alloc
|
|
||||||
|
|
||||||
if btStr != stdinSelector && btStr != "" {
|
if btStr != stdinSelector && btStr != "" {
|
||||||
if err := readFile(btStr, "BT", &inputData.BT); err != nil {
|
if err := readFile(btStr, "BT", &inputData.BT); err != nil {
|
||||||
|
|
@ -223,22 +223,57 @@ func Transition(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Dump the execution result
|
// Dump the execution result.
|
||||||
var (
|
var (
|
||||||
collector = make(Alloc)
|
collector Alloc
|
||||||
btleaves map[common.Hash]hexutil.Bytes
|
btleaves map[common.Hash]hexutil.Bytes
|
||||||
)
|
)
|
||||||
isBinary := chainConfig.IsUBT(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp)
|
isBinary := chainConfig.IsUBT(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp)
|
||||||
if !isBinary {
|
allocOutput := ctx.String(OutputAllocFlag.Name)
|
||||||
|
switch {
|
||||||
|
case !isBinary && allocOutput != "" && allocOutput != "stdout" && allocOutput != "stderr":
|
||||||
|
// Stream directly to the output file to avoid materializing the
|
||||||
|
// whole post-state in memory. dispatchOutput is told to skip alloc
|
||||||
|
// by clearing the output name.
|
||||||
|
if err := writeStreamedAlloc(filepath.Join(baseDir, allocOutput), s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
allocOutput = ""
|
||||||
|
case !isBinary:
|
||||||
|
collector = make(Alloc)
|
||||||
s.DumpToCollector(collector, nil)
|
s.DumpToCollector(collector, nil)
|
||||||
} else {
|
default:
|
||||||
btleaves = make(map[common.Hash]hexutil.Bytes)
|
btleaves = make(map[common.Hash]hexutil.Bytes)
|
||||||
if err := s.DumpBinTrieLeaves(btleaves); err != nil {
|
if err := s.DumpBinTrieLeaves(btleaves); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return dispatchOutput(ctx, baseDir, result, collector, allocOutput, body, btleaves)
|
||||||
|
}
|
||||||
|
|
||||||
return dispatchOutput(ctx, baseDir, result, collector, body, btleaves)
|
// writeStreamedAlloc writes the post-state alloc to path one account at a
|
||||||
|
// time, producing the same JSON shape as saveFile on an Alloc map.
|
||||||
|
func writeStreamedAlloc(path string, s *state.StateDB) error {
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return NewError(ErrorIO, fmt.Errorf("failed creating alloc output file: %v", err))
|
||||||
|
}
|
||||||
|
bw := bufio.NewWriter(f)
|
||||||
|
sa := newStreamingAlloc(bw)
|
||||||
|
s.DumpToCollector(sa, nil)
|
||||||
|
if err := sa.Close(); err != nil {
|
||||||
|
f.Close()
|
||||||
|
return NewError(ErrorIO, fmt.Errorf("failed writing alloc output: %v", err))
|
||||||
|
}
|
||||||
|
if err := bw.Flush(); err != nil {
|
||||||
|
f.Close()
|
||||||
|
return NewError(ErrorIO, fmt.Errorf("failed flushing alloc output: %v", err))
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
return NewError(ErrorIO, fmt.Errorf("failed closing alloc output file: %v", err))
|
||||||
|
}
|
||||||
|
log.Info("Wrote file", "file", path)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
||||||
|
|
@ -327,6 +362,10 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
||||||
if addr == nil {
|
if addr == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
g[*addr] = dumpAccountToTypesAccount(dumpAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpAccountToTypesAccount(dumpAccount state.DumpAccount) types.Account {
|
||||||
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
|
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
|
||||||
var storage map[common.Hash]common.Hash
|
var storage map[common.Hash]common.Hash
|
||||||
if dumpAccount.Storage != nil {
|
if dumpAccount.Storage != nil {
|
||||||
|
|
@ -335,13 +374,64 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
||||||
storage[k] = common.HexToHash(v)
|
storage[k] = common.HexToHash(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
genesisAccount := types.Account{
|
return types.Account{
|
||||||
Code: dumpAccount.Code,
|
Code: dumpAccount.Code,
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
Balance: balance,
|
Balance: balance,
|
||||||
Nonce: dumpAccount.Nonce,
|
Nonce: dumpAccount.Nonce,
|
||||||
}
|
}
|
||||||
g[*addr] = genesisAccount
|
}
|
||||||
|
|
||||||
|
// streamingAlloc is a DumpCollector that writes each account to w as it is
|
||||||
|
// visited, emitting a single JSON object keyed by address. Close must be
|
||||||
|
// called to emit the closing brace.
|
||||||
|
type streamingAlloc struct {
|
||||||
|
w io.Writer
|
||||||
|
wroteOne bool
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStreamingAlloc(w io.Writer) *streamingAlloc {
|
||||||
|
return &streamingAlloc{w: w}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *streamingAlloc) write(b []byte) {
|
||||||
|
if s.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, s.err = s.w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *streamingAlloc) OnRoot(common.Hash) {
|
||||||
|
s.write([]byte{'{'})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *streamingAlloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
||||||
|
if s.err != nil || addr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keyJSON, err := json.Marshal(*addr)
|
||||||
|
if err != nil {
|
||||||
|
s.err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
valueJSON, err := json.Marshal(dumpAccountToTypesAccount(dumpAccount))
|
||||||
|
if err != nil {
|
||||||
|
s.err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.wroteOne {
|
||||||
|
s.write([]byte{','})
|
||||||
|
}
|
||||||
|
s.write(keyJSON)
|
||||||
|
s.write([]byte{':'})
|
||||||
|
s.write(valueJSON)
|
||||||
|
s.wroteOne = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *streamingAlloc) Close() error {
|
||||||
|
s.write([]byte{'}'})
|
||||||
|
return s.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveFile marshals the object to the given file
|
// saveFile marshals the object to the given file
|
||||||
|
|
@ -359,8 +449,9 @@ func saveFile(baseDir, filename string, data interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatchOutput writes the output data to either stderr or stdout, or to the specified
|
// dispatchOutput writes the output data to either stderr or stdout, or to the specified
|
||||||
// files
|
// files. An empty allocOutput skips the alloc dispatch, which is used when the
|
||||||
func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes, bt map[common.Hash]hexutil.Bytes) error {
|
// alloc has already been streamed to disk by the caller.
|
||||||
|
func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, allocOutput string, body hexutil.Bytes, bt map[common.Hash]hexutil.Bytes) error {
|
||||||
stdOutObject := make(map[string]interface{})
|
stdOutObject := make(map[string]interface{})
|
||||||
stdErrObject := make(map[string]interface{})
|
stdErrObject := make(map[string]interface{})
|
||||||
dispatch := func(baseDir, fName, name string, obj interface{}) error {
|
dispatch := func(baseDir, fName, name string, obj interface{}) error {
|
||||||
|
|
@ -378,7 +469,7 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
|
if err := dispatch(baseDir, allocOutput, "alloc", alloc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
|
if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
|
||||||
|
|
|
||||||
247
core/state/reader_eip_7928.go
Normal file
247
core/state/reader_eip_7928.go
Normal file
|
|
@ -0,0 +1,247 @@
|
||||||
|
// Copyright 2026 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The EIP27928 reader utilizes a hierarchical architecture to optimize state
|
||||||
|
// access during block execution:
|
||||||
|
//
|
||||||
|
// - Base layer: The reader is initialized with the pre-transition state root,
|
||||||
|
// providing the access of the state.
|
||||||
|
//
|
||||||
|
// - Prefetching Layer: This base reader is wrapped by newPrefetchStateReader.
|
||||||
|
// Using an Access List hint, it asynchronously fetches required state data
|
||||||
|
// in the background, minimizing I/O blocking during transaction processing.
|
||||||
|
//
|
||||||
|
// - Execution Layer: To support parallel transaction execution within the EIP
|
||||||
|
// 7928 context, readers are wrapped in ReaderWithBlockLevelAccessList.
|
||||||
|
// This layer provides a "unified view" by merging the pre-transition state
|
||||||
|
// with mutated states from preceding transactions in the block.
|
||||||
|
//
|
||||||
|
// - Tracking Layer: Finally, the readerTracker wraps the execution reader to
|
||||||
|
// capture all state reads made during a specific transaction. These individual
|
||||||
|
// reads are subsequently merged to construct a comprehensive access list
|
||||||
|
// for the entire block.
|
||||||
|
//
|
||||||
|
// The architecture can be illustrated by the diagram below:
|
||||||
|
//
|
||||||
|
// ┌──────────────┴──────────────┐ ┌──────────────┴──────────────┐
|
||||||
|
// │ ReaderWithBlockLevelAL │ │ ReaderWithBlockLevelAL │
|
||||||
|
// │ (Pre-state + Mutations) │ │ (Pre-state + Mutations) │
|
||||||
|
// └──────────────┬──────────────┘ └──────────────┬──────────────┘
|
||||||
|
// │ │
|
||||||
|
// └────────────────┬─────────────────┘
|
||||||
|
// │
|
||||||
|
// ┌──────────────┴──────────────┐
|
||||||
|
// │ newPrefetchStateReader │ (Async I/O)
|
||||||
|
// │ (Access List Hint driven) │
|
||||||
|
// └──────────────┬──────────────┘
|
||||||
|
// │
|
||||||
|
// ┌──────────────┴──────────────┐
|
||||||
|
// │ Base Reader │ (State Root)
|
||||||
|
// │ (State & Contract Code) │
|
||||||
|
// └─────────────────────────────┘
|
||||||
|
|
||||||
|
// Note: The block producer, which is responsible for generating the block
|
||||||
|
// along with the block-level access list, does not maintain the internal
|
||||||
|
// hierarchy (e.g., PrefetchStateReader or ReaderWithBlockLevelAL).
|
||||||
|
// Instead, it directly utilizes the readerTracker, wrapped around the
|
||||||
|
// base reader, to construct the access list.
|
||||||
|
|
||||||
|
type fetchTask struct {
|
||||||
|
addr common.Address
|
||||||
|
slots []common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *fetchTask) weight() int { return 1 + len(t.slots) }
|
||||||
|
|
||||||
|
type prefetchStateReader struct {
|
||||||
|
StateReader
|
||||||
|
|
||||||
|
tasks []*fetchTask
|
||||||
|
nThreads int
|
||||||
|
done chan struct{}
|
||||||
|
term chan struct{}
|
||||||
|
closeOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:unused
|
||||||
|
func newPrefetchStateReader(reader StateReader, accessList map[common.Address][]common.Hash, nThreads int) *prefetchStateReader {
|
||||||
|
tasks := make([]*fetchTask, 0, len(accessList))
|
||||||
|
for addr, slots := range accessList {
|
||||||
|
tasks = append(tasks, &fetchTask{
|
||||||
|
addr: addr,
|
||||||
|
slots: slots,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return newPrefetchStateReaderInternal(reader, tasks, nThreads)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPrefetchStateReaderInternal(reader StateReader, tasks []*fetchTask, nThreads int) *prefetchStateReader {
|
||||||
|
r := &prefetchStateReader{
|
||||||
|
StateReader: reader,
|
||||||
|
tasks: tasks,
|
||||||
|
nThreads: nThreads,
|
||||||
|
done: make(chan struct{}),
|
||||||
|
term: make(chan struct{}),
|
||||||
|
}
|
||||||
|
go r.prefetch()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *prefetchStateReader) Close() {
|
||||||
|
r.closeOnce.Do(func() {
|
||||||
|
close(r.term)
|
||||||
|
<-r.done
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *prefetchStateReader) Wait() error {
|
||||||
|
select {
|
||||||
|
case <-r.term:
|
||||||
|
return nil
|
||||||
|
case <-r.done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *prefetchStateReader) prefetch() {
|
||||||
|
defer close(r.done)
|
||||||
|
|
||||||
|
if len(r.tasks) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var total int
|
||||||
|
for _, t := range r.tasks {
|
||||||
|
total += t.weight()
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
wg sync.WaitGroup
|
||||||
|
unit = (total + r.nThreads - 1) / r.nThreads // round-up the per worker unit
|
||||||
|
)
|
||||||
|
for i := 0; i < r.nThreads; i++ {
|
||||||
|
start := i * unit
|
||||||
|
if start >= total {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
limit := (i + 1) * unit
|
||||||
|
if i == r.nThreads-1 {
|
||||||
|
limit = total
|
||||||
|
}
|
||||||
|
// Schedule the worker for prefetching, the items on the range [start, limit)
|
||||||
|
// is exclusively assigned for this worker.
|
||||||
|
wg.Add(1)
|
||||||
|
go func(workerID, startW, endW int) {
|
||||||
|
r.process(startW, endW)
|
||||||
|
wg.Done()
|
||||||
|
}(i, start, limit)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *prefetchStateReader) process(start, limit int) {
|
||||||
|
var total = 0
|
||||||
|
for _, t := range r.tasks {
|
||||||
|
tw := t.weight()
|
||||||
|
if total+tw > start {
|
||||||
|
s := 0
|
||||||
|
if start > total {
|
||||||
|
s = start - total
|
||||||
|
}
|
||||||
|
l := tw
|
||||||
|
if limit < total+tw {
|
||||||
|
l = limit - total
|
||||||
|
}
|
||||||
|
for j := s; j < l; j++ {
|
||||||
|
select {
|
||||||
|
case <-r.term:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
if j == 0 {
|
||||||
|
r.StateReader.Account(t.addr)
|
||||||
|
} else {
|
||||||
|
r.StateReader.Storage(t.addr, t.slots[j-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total += tw
|
||||||
|
if total >= limit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReaderWithBlockLevelAccessList provides state access that reflects the
|
||||||
|
// pre-transition state combined with the mutations made by transactions
|
||||||
|
// prior to TxIndex.
|
||||||
|
type ReaderWithBlockLevelAccessList struct {
|
||||||
|
Reader
|
||||||
|
AccessList *bal.ConstructionBlockAccessList
|
||||||
|
TxIndex int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReaderWithBlockLevelAccessList constructs a reader for accessing states
|
||||||
|
// with the mutations made by transactions prior to txIndex.
|
||||||
|
//
|
||||||
|
// The txIndex refers to the call frame as such:
|
||||||
|
// - 0 for pre‑execution system contract calls.
|
||||||
|
// - 1 … n for transactions (in block order).
|
||||||
|
// - n + 1 for post‑execution system contract calls.
|
||||||
|
func NewReaderWithBlockLevelAccessList(base Reader, accessList *bal.ConstructionBlockAccessList, txIndex int) *ReaderWithBlockLevelAccessList {
|
||||||
|
return &ReaderWithBlockLevelAccessList{
|
||||||
|
Reader: base,
|
||||||
|
AccessList: accessList,
|
||||||
|
TxIndex: txIndex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account implements Reader, returning the account with the specific address.
|
||||||
|
func (r *ReaderWithBlockLevelAccessList) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage implements Reader, returning the storage slot with the specific
|
||||||
|
// address and slot key.
|
||||||
|
func (r *ReaderWithBlockLevelAccessList) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has implements Reader, returning the flag indicating whether the contract
|
||||||
|
// code with specified address and hash exists or not.
|
||||||
|
func (r *ReaderWithBlockLevelAccessList) Has(addr common.Address, codeHash common.Hash) bool {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code implements Reader, returning the contract code with specified address
|
||||||
|
// and hash.
|
||||||
|
func (r *ReaderWithBlockLevelAccessList) Code(addr common.Address, codeHash common.Hash) ([]byte, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeSize implements Reader, returning the contract code size with specified
|
||||||
|
// address and hash.
|
||||||
|
func (r *ReaderWithBlockLevelAccessList) CodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
145
core/state/reader_eip_7928_test.go
Normal file
145
core/state/reader_eip_7928_test.go
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
// Copyright 2026 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/testrand"
|
||||||
|
)
|
||||||
|
|
||||||
|
type countingStateReader struct {
|
||||||
|
accounts map[common.Address]int
|
||||||
|
storages map[common.Address]map[common.Hash]int
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRefStateReader() *countingStateReader {
|
||||||
|
return &countingStateReader{
|
||||||
|
accounts: make(map[common.Address]int),
|
||||||
|
storages: make(map[common.Address]map[common.Hash]int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *countingStateReader) validate(total int) error {
|
||||||
|
var sum int
|
||||||
|
for addr, n := range r.accounts {
|
||||||
|
if n != 1 {
|
||||||
|
return fmt.Errorf("duplicated account access: %x-%d", addr, n)
|
||||||
|
}
|
||||||
|
sum += 1
|
||||||
|
|
||||||
|
slots, exists := r.storages[addr]
|
||||||
|
if !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for key, n := range slots {
|
||||||
|
if n != 1 {
|
||||||
|
return fmt.Errorf("duplicated storage access: %x-%x-%d", addr, key, n)
|
||||||
|
}
|
||||||
|
sum += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for addr := range r.storages {
|
||||||
|
_, exists := r.accounts[addr]
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("dangling storage access: %x", addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sum != total {
|
||||||
|
return fmt.Errorf("unexpected number of access, want: %d, got: %d", total, sum)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *countingStateReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
|
r.accounts[addr] += 1
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (r *countingStateReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
|
slots, exists := r.storages[addr]
|
||||||
|
if !exists {
|
||||||
|
slots = make(map[common.Hash]int)
|
||||||
|
r.storages[addr] = slots
|
||||||
|
}
|
||||||
|
slots[slot] += 1
|
||||||
|
return common.Hash{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFetchTasks(n int) ([]*fetchTask, int) {
|
||||||
|
var (
|
||||||
|
total int
|
||||||
|
tasks []*fetchTask
|
||||||
|
)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
var slots []common.Hash
|
||||||
|
if rand.Intn(3) != 0 {
|
||||||
|
for j := 0; j < rand.Intn(100); j++ {
|
||||||
|
slots = append(slots, testrand.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks = append(tasks, &fetchTask{
|
||||||
|
addr: testrand.Address(),
|
||||||
|
slots: slots,
|
||||||
|
})
|
||||||
|
total += len(slots) + 1
|
||||||
|
}
|
||||||
|
return tasks, total
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrefetchReader(t *testing.T) {
|
||||||
|
type suite struct {
|
||||||
|
tasks []*fetchTask
|
||||||
|
threads int
|
||||||
|
total int
|
||||||
|
}
|
||||||
|
var suites []suite
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
tasks, total := makeFetchTasks(100)
|
||||||
|
suites = append(suites, suite{
|
||||||
|
tasks: tasks,
|
||||||
|
threads: rand.Intn(30) + 1,
|
||||||
|
total: total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// num(tasks) < num(threads)
|
||||||
|
tasks, total := makeFetchTasks(1)
|
||||||
|
suites = append(suites, suite{
|
||||||
|
tasks: tasks,
|
||||||
|
threads: 100,
|
||||||
|
total: total,
|
||||||
|
})
|
||||||
|
for _, s := range suites {
|
||||||
|
r := newRefStateReader()
|
||||||
|
pr := newPrefetchStateReaderInternal(r, s.tasks, s.threads)
|
||||||
|
pr.Wait()
|
||||||
|
if err := r.validate(s.total); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -162,8 +162,11 @@ func (s *stateObject) getPrefetchedTrie() Trie {
|
||||||
|
|
||||||
// GetState retrieves a value associated with the given storage key.
|
// GetState retrieves a value associated with the given storage key.
|
||||||
func (s *stateObject) GetState(key common.Hash) common.Hash {
|
func (s *stateObject) GetState(key common.Hash) common.Hash {
|
||||||
value, _ := s.getState(key)
|
value, dirty := s.dirtyStorage[key]
|
||||||
return value
|
if dirty {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return s.GetCommittedState(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getState retrieves a value associated with the given storage key, along with
|
// getState retrieves a value associated with the given storage key, along with
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
|
||||||
}
|
}
|
||||||
// Execute the message to preload the implicit touched states
|
// Execute the message to preload the implicit touched states
|
||||||
evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), stateCpy, p.config, cfg)
|
evm := vm.NewEVM(NewEVMBlockContext(header, p.chain, nil), stateCpy, p.config, cfg)
|
||||||
|
defer evm.Release()
|
||||||
|
|
||||||
// Convert the transaction into an executable message and pre-cache its sender
|
// Convert the transaction into an executable message and pre-cache its sender
|
||||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
|
||||||
// Apply pre-execution system calls.
|
// Apply pre-execution system calls.
|
||||||
context = NewEVMBlockContext(header, p.chain, nil)
|
context = NewEVMBlockContext(header, p.chain, nil)
|
||||||
evm := vm.NewEVM(context, tracingStateDB, config, cfg)
|
evm := vm.NewEVM(context, tracingStateDB, config, cfg)
|
||||||
|
defer evm.Release()
|
||||||
|
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
ProcessBeaconBlockRoot(*beaconRoot, evm)
|
ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
|
|
|
||||||
|
|
@ -344,9 +344,10 @@ func (st *stateTransition) preCheck() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
||||||
|
isAmsterdam := st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
||||||
if !msg.SkipTransactionChecks {
|
if !msg.SkipTransactionChecks {
|
||||||
// Verify tx gas limit does not exceed EIP-7825 cap.
|
// Verify tx gas limit does not exceed EIP-7825 cap.
|
||||||
if isOsaka && msg.GasLimit > params.MaxTxGas {
|
if isOsaka && !isAmsterdam && msg.GasLimit > params.MaxTxGas {
|
||||||
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
|
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
|
||||||
}
|
}
|
||||||
// Make sure the sender is an EOA
|
// Make sure the sender is an EOA
|
||||||
|
|
|
||||||
|
|
@ -1222,8 +1222,10 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
|
||||||
pool.mu.Lock()
|
pool.mu.Lock()
|
||||||
if reset != nil {
|
if reset != nil {
|
||||||
if reset.newHead != nil && reset.oldHead != nil {
|
if reset.newHead != nil && reset.oldHead != nil {
|
||||||
// Discard the transactions with the gas limit higher than the cap.
|
// Discard the transactions with the gas limit higher than the cap at the
|
||||||
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) && !pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
|
// Osaka fork boundary.
|
||||||
|
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) &&
|
||||||
|
!pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
|
||||||
var hashes []common.Hash
|
var hashes []common.Hash
|
||||||
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
|
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
|
||||||
if tx.Gas() > params.MaxTxGas {
|
if tx.Gas() > params.MaxTxGas {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rules.IsOsaka && tx.Gas() > params.MaxTxGas {
|
if rules.IsOsaka && !rules.IsAmsterdam && tx.Gas() > params.MaxTxGas {
|
||||||
return fmt.Errorf("%w (cap: %d, tx: %d)", core.ErrGasLimitTooHigh, params.MaxTxGas, tx.Gas())
|
return fmt.Errorf("%w (cap: %d, tx: %d)", core.ErrGasLimitTooHigh, params.MaxTxGas, tx.Gas())
|
||||||
}
|
}
|
||||||
// Transactions can't be negative. This may never happen using RLP decoded
|
// Transactions can't be negative. This may never happen using RLP decoded
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var activators = map[int]func(*JumpTable){
|
var activators = map[int]func(*JumpTable){
|
||||||
|
|
@ -92,8 +91,7 @@ func enable1884(jt *JumpTable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opSelfBalance(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opSelfBalance(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
balance := evm.StateDB.GetBalance(scope.Contract.Address())
|
scope.Stack.get().Set(evm.StateDB.GetBalance(scope.Contract.Address()))
|
||||||
scope.Stack.push(balance)
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,8 +109,7 @@ func enable1344(jt *JumpTable) {
|
||||||
|
|
||||||
// opChainID implements CHAINID opcode
|
// opChainID implements CHAINID opcode
|
||||||
func opChainID(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opChainID(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
chainId, _ := uint256.FromBig(evm.chainConfig.ChainID)
|
scope.Stack.get().SetFromBig(evm.chainConfig.ChainID)
|
||||||
scope.Stack.push(chainId)
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,8 +219,7 @@ func opTstore(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
|
|
||||||
// opBaseFee implements BASEFEE opcode
|
// opBaseFee implements BASEFEE opcode
|
||||||
func opBaseFee(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opBaseFee(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
baseFee, _ := uint256.FromBig(evm.Context.BaseFee)
|
scope.Stack.get().SetFromBig(evm.Context.BaseFee)
|
||||||
scope.Stack.push(baseFee)
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,7 +236,7 @@ func enable3855(jt *JumpTable) {
|
||||||
|
|
||||||
// opPush0 implements the PUSH0 opcode
|
// opPush0 implements the PUSH0 opcode
|
||||||
func opPush0(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opPush0(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int))
|
scope.Stack.get().Clear()
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,8 +287,7 @@ func opBlobHash(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
|
|
||||||
// opBlobBaseFee implements BLOBBASEFEE opcode
|
// opBlobBaseFee implements BLOBBASEFEE opcode
|
||||||
func opBlobBaseFee(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opBlobBaseFee(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
blobBaseFee, _ := uint256.FromBig(evm.Context.BlobBaseFee)
|
scope.Stack.get().SetFromBig(evm.Context.BlobBaseFee)
|
||||||
scope.Stack.push(blobBaseFee)
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -397,11 +392,11 @@ func opExtCodeCopyEIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, er
|
||||||
func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
codeLen = uint64(len(scope.Contract.Code))
|
codeLen = uint64(len(scope.Contract.Code))
|
||||||
integer = new(uint256.Int)
|
elem = scope.Stack.get()
|
||||||
)
|
)
|
||||||
*pc += 1
|
*pc += 1
|
||||||
if *pc < codeLen {
|
if *pc < codeLen {
|
||||||
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
elem.SetUint64(uint64(scope.Contract.Code[*pc]))
|
||||||
|
|
||||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall && *pc%31 == 0 {
|
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall && *pc%31 == 0 {
|
||||||
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
||||||
|
|
@ -414,7 +409,7 @@ func opPush1EIP4762(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scope.Stack.push(integer.Clear())
|
elem.Clear()
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -426,12 +421,11 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
||||||
start = min(codeLen, int(*pc+1))
|
start = min(codeLen, int(*pc+1))
|
||||||
end = min(codeLen, start+pushByteSize)
|
end = min(codeLen, start+pushByteSize)
|
||||||
)
|
)
|
||||||
scope.Stack.push(new(uint256.Int).SetBytes(
|
scope.Stack.get().SetBytes(
|
||||||
common.RightPadBytes(
|
common.RightPadBytes(
|
||||||
scope.Contract.Code[start:end],
|
scope.Contract.Code[start:end],
|
||||||
pushByteSize,
|
pushByteSize,
|
||||||
)),
|
))
|
||||||
)
|
|
||||||
|
|
||||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||||
contractAddr := scope.Contract.Address()
|
contractAddr := scope.Contract.Address()
|
||||||
|
|
@ -583,7 +577,7 @@ func enable7702(jt *JumpTable) {
|
||||||
|
|
||||||
// opSlotNum enables the SLOTNUM opcode
|
// opSlotNum enables the SLOTNUM opcode
|
||||||
func opSlotNum(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opSlotNum(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(uint256.NewInt(evm.Context.SlotNum))
|
scope.Stack.get().SetUint64(evm.Context.SlotNum)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,8 @@ type EVM struct {
|
||||||
|
|
||||||
readOnly bool // Whether to throw on stateful modifications
|
readOnly bool // Whether to throw on stateful modifications
|
||||||
returnData []byte // Last CALL's return data for subsequent reuse
|
returnData []byte // Last CALL's return data for subsequent reuse
|
||||||
|
|
||||||
|
arena *stackArena
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEVM constructs an EVM instance with the supplied block context, state
|
// NewEVM constructs an EVM instance with the supplied block context, state
|
||||||
|
|
@ -141,6 +143,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon
|
||||||
chainConfig: chainConfig,
|
chainConfig: chainConfig,
|
||||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
|
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
|
||||||
jumpDests: newGlobalJumpDests(),
|
jumpDests: newGlobalJumpDests(),
|
||||||
|
arena: newArena(),
|
||||||
}
|
}
|
||||||
evm.precompiles = activePrecompiledContracts(evm.chainRules)
|
evm.precompiles = activePrecompiledContracts(evm.chainRules)
|
||||||
|
|
||||||
|
|
@ -223,6 +226,12 @@ func (evm *EVM) Cancel() {
|
||||||
evm.abort.Store(true)
|
evm.abort.Store(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Release returns some memory allocated by the EVM, should be called after the EVM was used
|
||||||
|
// for the last time. Not necessary, but an improvement.
|
||||||
|
func (evm *EVM) Release() {
|
||||||
|
returnStack(evm.arena)
|
||||||
|
}
|
||||||
|
|
||||||
// Cancelled returns true if Cancel has been called
|
// Cancelled returns true if Cancel has been called
|
||||||
func (evm *EVM) Cancelled() bool {
|
func (evm *EVM) Cancelled() bool {
|
||||||
return evm.abort.Load()
|
return evm.abort.Load()
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ func memoryCopierGas(stackpos int) gasFunc {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
// And gas for copying data, charged per word at param.CopyGas
|
// And gas for copying data, charged per word at param.CopyGas
|
||||||
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
|
words, overflow := stack.back(stackpos).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +100,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
||||||
return GasCosts{}, ErrWriteProtection
|
return GasCosts{}, ErrWriteProtection
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
y, x = stack.Back(1), stack.Back(0)
|
y, x = stack.back(1), stack.back(0)
|
||||||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
|
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
|
||||||
)
|
)
|
||||||
// The legacy gas metering only takes into consideration the current state
|
// The legacy gas metering only takes into consideration the current state
|
||||||
|
|
@ -192,7 +192,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
}
|
}
|
||||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||||
var (
|
var (
|
||||||
y, x = stack.Back(1), stack.Back(0)
|
y, x = stack.back(1), stack.back(0)
|
||||||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
|
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
|
||||||
)
|
)
|
||||||
value := common.Hash(y.Bytes32())
|
value := common.Hash(y.Bytes32())
|
||||||
|
|
@ -228,7 +228,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
|
|
||||||
func makeGasLog(n uint64) gasFunc {
|
func makeGasLog(n uint64) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
|
requestedSize, overflow := stack.back(1).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +261,7 @@ func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
|
wordGas, overflow := stack.back(1).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
@ -299,7 +299,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
|
wordGas, overflow := stack.back(2).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
@ -317,7 +317,7 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
size, overflow := stack.back(2).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +336,7 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
size, overflow := stack.back(2).Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
@ -352,7 +352,7 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
expByteLen := uint64((stack.back(1).BitLen() + 7) / 8)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
|
gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
|
||||||
|
|
@ -365,7 +365,7 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
|
||||||
}
|
}
|
||||||
|
|
||||||
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
expByteLen := uint64((stack.back(1).BitLen() + 7) / 8)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
|
gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
|
||||||
|
|
@ -390,7 +390,7 @@ func makeCallVariantGasCost(intrinsicFunc gasFunc) gasFunc {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic.RegularGas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsic.RegularGas, stack.back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -405,8 +405,8 @@ func makeCallVariantGasCost(intrinsicFunc gasFunc) gasFunc {
|
||||||
func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
var (
|
var (
|
||||||
gas uint64
|
gas uint64
|
||||||
transfersValue = !stack.Back(2).IsZero()
|
transfersValue = !stack.back(2).IsZero()
|
||||||
address = common.Address(stack.Back(1).Bytes20())
|
address = common.Address(stack.back(1).Bytes20())
|
||||||
)
|
)
|
||||||
if evm.readOnly && transfersValue {
|
if evm.readOnly && transfersValue {
|
||||||
return GasCosts{}, ErrWriteProtection
|
return GasCosts{}, ErrWriteProtection
|
||||||
|
|
@ -453,7 +453,7 @@ func gasCallCodeIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memor
|
||||||
gas uint64
|
gas uint64
|
||||||
overflow bool
|
overflow bool
|
||||||
)
|
)
|
||||||
if stack.Back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 {
|
if stack.back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 {
|
||||||
gas += params.CallValueTransferGas
|
gas += params.CallValueTransferGas
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
|
|
@ -487,7 +487,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
// EIP150 homestead gas reprice fork:
|
// EIP150 homestead gas reprice fork:
|
||||||
if evm.chainRules.IsEIP150 {
|
if evm.chainRules.IsEIP150 {
|
||||||
gas = params.SelfdestructGasEIP150
|
gas = params.SelfdestructGasEIP150
|
||||||
var address = common.Address(stack.Back(0).Bytes20())
|
var address = common.Address(stack.back(0).Bytes20())
|
||||||
|
|
||||||
if evm.chainRules.IsEIP158 {
|
if evm.chainRules.IsEIP158 {
|
||||||
// if empty and transfers value
|
// if empty and transfers value
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func opAdd(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opAdd(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
|
|
@ -244,7 +243,7 @@ func opKeccak256(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opAddress(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opAddress(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
|
scope.Stack.get().SetBytes(scope.Contract.Address().Bytes())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,17 +255,17 @@ func opBalance(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opOrigin(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opOrigin(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetBytes(evm.Origin.Bytes()))
|
scope.Stack.get().SetBytes(evm.Origin.Bytes())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCaller(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opCaller(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
|
scope.Stack.get().SetBytes(scope.Contract.Caller().Bytes())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCallValue(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opCallValue(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(scope.Contract.value)
|
scope.Stack.get().Set(scope.Contract.value)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,7 +281,7 @@ func opCallDataLoad(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCallDataSize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opCallDataSize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input))))
|
scope.Stack.get().SetUint64(uint64(len(scope.Contract.Input)))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,7 +304,7 @@ func opCallDataCopy(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opReturnDataSize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opReturnDataSize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(evm.returnData))))
|
scope.Stack.get().SetUint64(uint64(len(evm.returnData)))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,7 +337,7 @@ func opExtCodeSize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCodeSize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opCodeSize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code))))
|
scope.Stack.get().SetUint64(uint64(len(scope.Contract.Code)))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -416,7 +415,7 @@ func opExtCodeHash(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opGasprice(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opGasprice(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(evm.GasPrice.Clone())
|
scope.Stack.get().Set(evm.GasPrice)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,35 +450,32 @@ func opBlockhash(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opCoinbase(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opCoinbase(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetBytes(evm.Context.Coinbase.Bytes()))
|
scope.Stack.get().SetBytes(evm.Context.Coinbase.Bytes())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opTimestamp(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opTimestamp(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetUint64(evm.Context.Time))
|
scope.Stack.get().SetUint64(evm.Context.Time)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opNumber(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opNumber(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
v, _ := uint256.FromBig(evm.Context.BlockNumber)
|
scope.Stack.get().SetFromBig(evm.Context.BlockNumber)
|
||||||
scope.Stack.push(v)
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opDifficulty(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opDifficulty(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
v, _ := uint256.FromBig(evm.Context.Difficulty)
|
scope.Stack.get().SetFromBig(evm.Context.Difficulty)
|
||||||
scope.Stack.push(v)
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opRandom(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opRandom(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
v := new(uint256.Int).SetBytes(evm.Context.Random.Bytes())
|
scope.Stack.get().SetBytes(evm.Context.Random.Bytes())
|
||||||
scope.Stack.push(v)
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opGasLimit(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opGasLimit(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetUint64(evm.Context.GasLimit))
|
scope.Stack.get().SetUint64(evm.Context.GasLimit)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -556,17 +552,17 @@ func opJumpdest(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func opPc(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opPc(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetUint64(*pc))
|
scope.Stack.get().SetUint64(*pc)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opMsize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opMsize(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len())))
|
scope.Stack.get().SetUint64(uint64(scope.Memory.Len()))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func opGas(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opGas(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas.RegularGas))
|
scope.Stack.get().SetUint64(scope.Contract.Gas.RegularGas)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1007,7 +1003,7 @@ func opDupN(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//The n‘th stack item is duplicated at the top of the stack.
|
//The n‘th stack item is duplicated at the top of the stack.
|
||||||
scope.Stack.push(scope.Stack.Back(n - 1))
|
scope.Stack.push(scope.Stack.back(n - 1))
|
||||||
*pc += 1
|
*pc += 1
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1034,10 +1030,10 @@ func opSwapN(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
return nil, &ErrStackUnderflow{stackLen: scope.Stack.len(), required: n + 1}
|
return nil, &ErrStackUnderflow{stackLen: scope.Stack.len(), required: n + 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The (n+1)‘th stack item is swapped with the top of the stack.
|
// The (n+1)’th stack item is swapped with the top of the stack.
|
||||||
indexTop := scope.Stack.len() - 1
|
top := scope.Stack.peek()
|
||||||
indexN := scope.Stack.len() - 1 - n
|
nth := scope.Stack.back(n)
|
||||||
scope.Stack.data[indexTop], scope.Stack.data[indexN] = scope.Stack.data[indexN], scope.Stack.data[indexTop]
|
*top, *nth = *nth, *top
|
||||||
*pc += 1
|
*pc += 1
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1067,10 +1063,10 @@ func opExchange(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
return nil, &ErrStackUnderflow{stackLen: scope.Stack.len(), required: need}
|
return nil, &ErrStackUnderflow{stackLen: scope.Stack.len(), required: need}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The (n+1)‘th stack item is swapped with the (m+1)‘th stack item.
|
// The (n+1)’th stack item is swapped with the (m+1)’th stack item.
|
||||||
indexN := scope.Stack.len() - 1 - n
|
nth := scope.Stack.back(n)
|
||||||
indexM := scope.Stack.len() - 1 - m
|
mth := scope.Stack.back(m)
|
||||||
scope.Stack.data[indexN], scope.Stack.data[indexM] = scope.Stack.data[indexM], scope.Stack.data[indexN]
|
*nth, *mth = *mth, *nth
|
||||||
*pc += 1
|
*pc += 1
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1106,13 +1102,13 @@ func makeLog(size int) executionFunc {
|
||||||
func opPush1(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opPush1(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
codeLen = uint64(len(scope.Contract.Code))
|
codeLen = uint64(len(scope.Contract.Code))
|
||||||
integer = new(uint256.Int)
|
elem = scope.Stack.get()
|
||||||
)
|
)
|
||||||
*pc += 1
|
*pc += 1
|
||||||
if *pc < codeLen {
|
if *pc < codeLen {
|
||||||
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
elem.SetUint64(uint64(scope.Contract.Code[*pc]))
|
||||||
} else {
|
} else {
|
||||||
scope.Stack.push(integer.Clear())
|
elem.Clear()
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1121,14 +1117,14 @@ func opPush1(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
func opPush2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
func opPush2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
codeLen = uint64(len(scope.Contract.Code))
|
codeLen = uint64(len(scope.Contract.Code))
|
||||||
integer = new(uint256.Int)
|
elem = scope.Stack.get()
|
||||||
)
|
)
|
||||||
if *pc+2 < codeLen {
|
if *pc+2 < codeLen {
|
||||||
scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3]))
|
elem.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3])
|
||||||
} else if *pc+1 < codeLen {
|
} else if *pc+1 < codeLen {
|
||||||
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8))
|
elem.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8)
|
||||||
} else {
|
} else {
|
||||||
scope.Stack.push(integer.Clear())
|
elem.Clear()
|
||||||
}
|
}
|
||||||
*pc += 2
|
*pc += 2
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -1142,13 +1138,13 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
||||||
start = min(codeLen, int(*pc+1))
|
start = min(codeLen, int(*pc+1))
|
||||||
end = min(codeLen, start+pushByteSize)
|
end = min(codeLen, start+pushByteSize)
|
||||||
)
|
)
|
||||||
a := new(uint256.Int).SetBytes(scope.Contract.Code[start:end])
|
a := scope.Stack.get()
|
||||||
|
a.SetBytes(scope.Contract.Code[start:end])
|
||||||
|
|
||||||
// Missing bytes: pushByteSize - len(pushData)
|
// Missing bytes: pushByteSize - len(pushData)
|
||||||
if missing := pushByteSize - (end - start); missing > 0 {
|
if missing := pushByteSize - (end - start); missing > 0 {
|
||||||
a.Lsh(a, uint(8*missing))
|
a.Lsh(a, uint(8*missing))
|
||||||
}
|
}
|
||||||
scope.Stack.push(a)
|
|
||||||
*pc += size
|
*pc += size
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
@ -98,7 +99,7 @@ func init() {
|
||||||
func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
|
func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -109,8 +110,8 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
|
||||||
stack.push(x)
|
stack.push(x)
|
||||||
stack.push(y)
|
stack.push(y)
|
||||||
opFn(&pc, evm, &ScopeContext{nil, stack, nil})
|
opFn(&pc, evm, &ScopeContext{nil, stack, nil})
|
||||||
if len(stack.data) != 1 {
|
if stack.len() != 1 {
|
||||||
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
|
t.Errorf("Expected one item on stack after %v, got %d: ", name, stack.len())
|
||||||
}
|
}
|
||||||
actual := stack.pop()
|
actual := stack.pop()
|
||||||
|
|
||||||
|
|
@ -196,7 +197,7 @@ func TestSAR(t *testing.T) {
|
||||||
func TestAddMod(t *testing.T) {
|
func TestAddMod(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
)
|
)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
@ -239,7 +240,7 @@ func TestWriteExpectedValues(t *testing.T) {
|
||||||
getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
|
getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
)
|
)
|
||||||
result := make([]TwoOperandTestcase, len(args))
|
result := make([]TwoOperandTestcase, len(args))
|
||||||
|
|
@ -282,23 +283,40 @@ func TestJsonTestcases(t *testing.T) {
|
||||||
|
|
||||||
func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
scope = &ScopeContext{nil, stack, nil}
|
code = []byte{}
|
||||||
|
opPush32 = makePush(32, 32)
|
||||||
)
|
)
|
||||||
// convert args
|
// convert args
|
||||||
intArgs := make([]*uint256.Int, len(args))
|
intArgs := make([]*uint256.Int, len(args))
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
|
code = append(code, common.LeftPadBytes(common.Hex2Bytes(arg), 32)...)
|
||||||
intArgs[i] = new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
|
intArgs[i] = new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
|
||||||
}
|
}
|
||||||
pc := uint64(0)
|
pc := uint64(0)
|
||||||
for bench.Loop() {
|
scope := &ScopeContext{nil, stack, &Contract{Code: code}}
|
||||||
for _, arg := range intArgs {
|
start := time.Now()
|
||||||
stack.push(arg)
|
bench.ResetTimer()
|
||||||
|
for i := 0; i < bench.N; i++ {
|
||||||
|
for range len(args) {
|
||||||
|
opPush32(&pc, evm, scope)
|
||||||
|
pc += 32
|
||||||
}
|
}
|
||||||
op(&pc, evm, scope)
|
op(&pc, evm, scope)
|
||||||
stack.pop()
|
opPop(&pc, evm, scope)
|
||||||
}
|
}
|
||||||
|
bench.StopTimer()
|
||||||
|
elapsed := uint64(time.Since(start))
|
||||||
|
if elapsed < 1 {
|
||||||
|
elapsed = 1
|
||||||
|
}
|
||||||
|
reqGas := uint64(len(args))*GasFastestStep + GasFastestStep + GasQuickStep
|
||||||
|
gasUsed := reqGas * uint64(bench.N)
|
||||||
|
bench.ReportMetric(float64(reqGas), "gas/op")
|
||||||
|
// Keep it as uint64, multiply 100 to get two digit float later
|
||||||
|
mgasps := (100 * 1000 * gasUsed) / elapsed
|
||||||
|
bench.ReportMetric(float64(mgasps)/100, "mgas/s")
|
||||||
|
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
want := new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
|
want := new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
|
||||||
|
|
@ -519,7 +537,7 @@ func BenchmarkOpIsZero(b *testing.B) {
|
||||||
func TestOpMstore(t *testing.T) {
|
func TestOpMstore(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
)
|
)
|
||||||
mem.Resize(64)
|
mem.Resize(64)
|
||||||
|
|
@ -542,7 +560,7 @@ func TestOpMstore(t *testing.T) {
|
||||||
func BenchmarkOpMstore(bench *testing.B) {
|
func BenchmarkOpMstore(bench *testing.B) {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
)
|
)
|
||||||
mem.Resize(64)
|
mem.Resize(64)
|
||||||
|
|
@ -561,7 +579,7 @@ func TestOpTstore(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
evm = NewEVM(BlockContext{}, statedb, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, statedb, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
caller = common.Address{}
|
caller = common.Address{}
|
||||||
to = common.Address{1}
|
to = common.Address{1}
|
||||||
|
|
@ -600,7 +618,7 @@ func TestOpTstore(t *testing.T) {
|
||||||
func BenchmarkOpKeccak256(bench *testing.B) {
|
func BenchmarkOpKeccak256(bench *testing.B) {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
)
|
)
|
||||||
mem.Resize(32)
|
mem.Resize(32)
|
||||||
|
|
@ -672,7 +690,7 @@ func TestCreate2Addresses(t *testing.T) {
|
||||||
codeHash := crypto.Keccak256(code)
|
codeHash := crypto.Keccak256(code)
|
||||||
address := crypto.CreateAddress2(origin, salt, codeHash)
|
address := crypto.CreateAddress2(origin, salt, codeHash)
|
||||||
/*
|
/*
|
||||||
stack := newstack()
|
stack := newStackForTesting()
|
||||||
// salt, but we don't need that for this test
|
// salt, but we don't need that for this test
|
||||||
stack.push(big.NewInt(int64(len(code)))) //size
|
stack.push(big.NewInt(int64(len(code)))) //size
|
||||||
stack.push(big.NewInt(0)) // memstart
|
stack.push(big.NewInt(0)) // memstart
|
||||||
|
|
@ -701,12 +719,12 @@ func TestRandom(t *testing.T) {
|
||||||
} {
|
} {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{Random: &tt.random}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{Random: &tt.random}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
)
|
)
|
||||||
opRandom(&pc, evm, &ScopeContext{nil, stack, nil})
|
opRandom(&pc, evm, &ScopeContext{nil, stack, nil})
|
||||||
if len(stack.data) != 1 {
|
if have, want := stack.len(), 1; have != want {
|
||||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
t.Errorf("test '%v': want %d item(s) on stack, have %d: ", tt.name, have, want)
|
||||||
}
|
}
|
||||||
actual := stack.pop()
|
actual := stack.pop()
|
||||||
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes()))
|
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes()))
|
||||||
|
|
@ -741,14 +759,14 @@ func TestBlobHash(t *testing.T) {
|
||||||
} {
|
} {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
)
|
)
|
||||||
evm.SetTxContext(TxContext{BlobHashes: tt.hashes})
|
evm.SetTxContext(TxContext{BlobHashes: tt.hashes})
|
||||||
stack.push(uint256.NewInt(tt.idx))
|
stack.push(uint256.NewInt(tt.idx))
|
||||||
opBlobHash(&pc, evm, &ScopeContext{nil, stack, nil})
|
opBlobHash(&pc, evm, &ScopeContext{nil, stack, nil})
|
||||||
if len(stack.data) != 1 {
|
if have, want := stack.len(), 1; have != want {
|
||||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
t.Errorf("test '%v': want %d item(s) on stack, have %d: ", tt.name, have, want)
|
||||||
}
|
}
|
||||||
actual := stack.pop()
|
actual := stack.pop()
|
||||||
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.expect.Bytes()))
|
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.expect.Bytes()))
|
||||||
|
|
@ -844,7 +862,7 @@ func TestOpMCopy(t *testing.T) {
|
||||||
} {
|
} {
|
||||||
var (
|
var (
|
||||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
)
|
)
|
||||||
data := common.FromHex(strings.ReplaceAll(tc.pre, " ", ""))
|
data := common.FromHex(strings.ReplaceAll(tc.pre, " ", ""))
|
||||||
|
|
@ -907,7 +925,7 @@ func TestPush(t *testing.T) {
|
||||||
|
|
||||||
scope := &ScopeContext{
|
scope := &ScopeContext{
|
||||||
Memory: nil,
|
Memory: nil,
|
||||||
Stack: newstack(),
|
Stack: newStackForTesting(),
|
||||||
Contract: &Contract{
|
Contract: &Contract{
|
||||||
Code: code,
|
Code: code,
|
||||||
},
|
},
|
||||||
|
|
@ -988,7 +1006,7 @@ func TestOpCLZ(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
// prepare a fresh stack and PC
|
// prepare a fresh stack and PC
|
||||||
stack := newstack()
|
stack := newStackForTesting()
|
||||||
pc := uint64(0)
|
pc := uint64(0)
|
||||||
|
|
||||||
// parse input
|
// parse input
|
||||||
|
|
@ -1111,7 +1129,7 @@ func TestEIP8024_Execution(t *testing.T) {
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
code := common.FromHex(tc.codeHex)
|
code := common.FromHex(tc.codeHex)
|
||||||
stack := newstack()
|
stack := newStackForTesting()
|
||||||
pc := uint64(0)
|
pc := uint64(0)
|
||||||
scope := &ScopeContext{Stack: stack, Contract: &Contract{Code: code}}
|
scope := &ScopeContext{Stack: stack, Contract: &Contract{Code: code}}
|
||||||
var err error
|
var err error
|
||||||
|
|
@ -1189,8 +1207,9 @@ func TestEIP8024_Execution(t *testing.T) {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
got := make([]uint64, 0, stack.len())
|
got := make([]uint64, 0, stack.len())
|
||||||
for i := stack.len() - 1; i >= 0; i-- {
|
data := stack.Data()
|
||||||
got = append(got, stack.data[i].Uint64())
|
for i := len(data) - 1; i >= 0; i-- {
|
||||||
|
got = append(got, data[i].Uint64())
|
||||||
}
|
}
|
||||||
if len(got) != len(tc.wantVals) {
|
if len(got) != len(tc.wantVals) {
|
||||||
t.Fatalf("stack len=%d; want %d", len(got), len(tc.wantVals))
|
t.Fatalf("stack len=%d; want %d", len(got), len(tc.wantVals))
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,8 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
var (
|
var (
|
||||||
op OpCode // current opcode
|
op OpCode // current opcode
|
||||||
jumpTable *JumpTable = evm.table
|
jumpTable *JumpTable = evm.table
|
||||||
mem = NewMemory() // bound memory
|
mem = NewMemory() // bound memory
|
||||||
stack = newstack() // local stack
|
stack = evm.arena.stack() // local stack
|
||||||
callContext = &ScopeContext{
|
callContext = &ScopeContext{
|
||||||
Memory: mem,
|
Memory: mem,
|
||||||
Stack: stack,
|
Stack: stack,
|
||||||
|
|
@ -140,7 +140,7 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
// so that it gets executed _after_: the OnOpcode needs the stacks before
|
// so that it gets executed _after_: the OnOpcode needs the stacks before
|
||||||
// they are returned to the pools
|
// they are returned to the pools
|
||||||
defer func() {
|
defer func() {
|
||||||
returnStack(stack)
|
stack.release()
|
||||||
mem.Free()
|
mem.Free()
|
||||||
}()
|
}()
|
||||||
contract.Input = input
|
contract.Input = input
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func BenchmarkInterpreter(b *testing.B) {
|
||||||
evm = NewEVM(BlockContext{BlockNumber: big.NewInt(1), Time: 1, Random: &common.Hash{}}, statedb, params.MergedTestChainConfig, Config{})
|
evm = NewEVM(BlockContext{BlockNumber: big.NewInt(1), Time: 1, Random: &common.Hash{}}, statedb, params.MergedTestChainConfig, Config{})
|
||||||
startGas uint64 = 100_000_000
|
startGas uint64 = 100_000_000
|
||||||
value = uint256.NewInt(0)
|
value = uint256.NewInt(0)
|
||||||
stack = newstack()
|
stack = newStackForTesting()
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
contract = NewContract(common.Address{}, common.Address{}, value, NewGasBudget(startGas), nil)
|
contract = NewContract(common.Address{}, common.Address{}, value, NewGasBudget(startGas), nil)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,59 +17,59 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
func memoryKeccak256(stack *Stack) (uint64, bool) {
|
func memoryKeccak256(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
return calcMemSize64(stack.back(0), stack.back(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCallDataCopy(stack *Stack) (uint64, bool) {
|
func memoryCallDataCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
return calcMemSize64(stack.back(0), stack.back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
|
func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
return calcMemSize64(stack.back(0), stack.back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCodeCopy(stack *Stack) (uint64, bool) {
|
func memoryCodeCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
return calcMemSize64(stack.back(0), stack.back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
|
func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(1), stack.Back(3))
|
return calcMemSize64(stack.back(1), stack.back(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMLoad(stack *Stack) (uint64, bool) {
|
func memoryMLoad(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64WithUint(stack.Back(0), 32)
|
return calcMemSize64WithUint(stack.back(0), 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMStore8(stack *Stack) (uint64, bool) {
|
func memoryMStore8(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64WithUint(stack.Back(0), 1)
|
return calcMemSize64WithUint(stack.back(0), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMStore(stack *Stack) (uint64, bool) {
|
func memoryMStore(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64WithUint(stack.Back(0), 32)
|
return calcMemSize64WithUint(stack.back(0), 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryMcopy(stack *Stack) (uint64, bool) {
|
func memoryMcopy(stack *Stack) (uint64, bool) {
|
||||||
mStart := stack.Back(0) // stack[0]: dest
|
mStart := stack.back(0) // stack[0]: dest
|
||||||
if stack.Back(1).Gt(mStart) {
|
if stack.back(1).Gt(mStart) {
|
||||||
mStart = stack.Back(1) // stack[1]: source
|
mStart = stack.back(1) // stack[1]: source
|
||||||
}
|
}
|
||||||
return calcMemSize64(mStart, stack.Back(2)) // stack[2]: length
|
return calcMemSize64(mStart, stack.back(2)) // stack[2]: length
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCreate(stack *Stack) (uint64, bool) {
|
func memoryCreate(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
return calcMemSize64(stack.back(1), stack.back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCreate2(stack *Stack) (uint64, bool) {
|
func memoryCreate2(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
return calcMemSize64(stack.back(1), stack.back(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryCall(stack *Stack) (uint64, bool) {
|
func memoryCall(stack *Stack) (uint64, bool) {
|
||||||
x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
|
x, overflow := calcMemSize64(stack.back(5), stack.back(6))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
|
y, overflow := calcMemSize64(stack.back(3), stack.back(4))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
|
|
@ -80,11 +80,11 @@ func memoryCall(stack *Stack) (uint64, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryDelegateCall(stack *Stack) (uint64, bool) {
|
func memoryDelegateCall(stack *Stack) (uint64, bool) {
|
||||||
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
x, overflow := calcMemSize64(stack.back(4), stack.back(5))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
y, overflow := calcMemSize64(stack.back(2), stack.back(3))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
|
|
@ -95,11 +95,11 @@ func memoryDelegateCall(stack *Stack) (uint64, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryStaticCall(stack *Stack) (uint64, bool) {
|
func memoryStaticCall(stack *Stack) (uint64, bool) {
|
||||||
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
|
x, overflow := calcMemSize64(stack.back(4), stack.back(5))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
y, overflow := calcMemSize64(stack.back(2), stack.back(3))
|
||||||
if overflow {
|
if overflow {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
|
|
@ -110,13 +110,13 @@ func memoryStaticCall(stack *Stack) (uint64, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryReturn(stack *Stack) (uint64, bool) {
|
func memoryReturn(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
return calcMemSize64(stack.back(0), stack.back(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryRevert(stack *Stack) (uint64, bool) {
|
func memoryRevert(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
return calcMemSize64(stack.back(0), stack.back(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func memoryLog(stack *Stack) (uint64, bool) {
|
func memoryLog(stack *Stack) (uint64, bool) {
|
||||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
return calcMemSize64(stack.back(0), stack.back(1))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||||
}
|
}
|
||||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||||
var (
|
var (
|
||||||
y, x = stack.Back(1), stack.peek()
|
y, x = stack.back(1), stack.peek()
|
||||||
slot = common.Hash(x.Bytes32())
|
slot = common.Hash(x.Bytes32())
|
||||||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
||||||
cost = uint64(0)
|
cost = uint64(0)
|
||||||
|
|
@ -158,7 +158,7 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
|
|
||||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
|
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
addr := common.Address(stack.Back(addressPosition).Bytes20())
|
addr := common.Address(stack.back(addressPosition).Bytes20())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
||||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
||||||
|
|
@ -269,7 +269,7 @@ func gasCallEIP7702(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
|
||||||
// Although it's checked in `gasCall`, EIP-7702 loads the target's code before
|
// Although it's checked in `gasCall`, EIP-7702 loads the target's code before
|
||||||
// to determine if it is resolving a delegation. This could incorrectly record
|
// to determine if it is resolving a delegation. This could incorrectly record
|
||||||
// the target in the block access list (BAL) if the call later fails.
|
// the target in the block access list (BAL) if the call later fails.
|
||||||
transfersValue := !stack.Back(2).IsZero()
|
transfersValue := !stack.back(2).IsZero()
|
||||||
if evm.readOnly && transfersValue {
|
if evm.readOnly && transfersValue {
|
||||||
return GasCosts{}, ErrWriteProtection
|
return GasCosts{}, ErrWriteProtection
|
||||||
}
|
}
|
||||||
|
|
@ -281,7 +281,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
||||||
var (
|
var (
|
||||||
eip2929Cost uint64
|
eip2929Cost uint64
|
||||||
eip7702Cost uint64
|
eip7702Cost uint64
|
||||||
addr = common.Address(stack.Back(1).Bytes20())
|
addr = common.Address(stack.back(1).Bytes20())
|
||||||
)
|
)
|
||||||
// Perform EIP-2929 checks (stateless), checking address presence
|
// Perform EIP-2929 checks (stateless), checking address presence
|
||||||
// in the accessList and charge the cold access accordingly.
|
// in the accessList and charge the cold access accordingly.
|
||||||
|
|
@ -330,7 +330,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
||||||
}
|
}
|
||||||
// Calculate the gas budget for the nested call. The costs defined by
|
// Calculate the gas budget for the nested call. The costs defined by
|
||||||
// EIP-2929 and EIP-7702 have already been applied.
|
// EIP-2929 and EIP-7702 have already been applied.
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost.RegularGas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost.RegularGas, stack.back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
|
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||||
var (
|
var (
|
||||||
target = common.Address(stack.Back(1).Bytes20())
|
target = common.Address(stack.back(1).Bytes20())
|
||||||
witnessGas uint64
|
witnessGas uint64
|
||||||
_, isPrecompile = evm.precompile(target)
|
_, isPrecompile = evm.precompile(target)
|
||||||
isSystemContract = target == params.HistoryStorageAddress
|
isSystemContract = target == params.HistoryStorageAddress
|
||||||
|
|
@ -64,7 +64,7 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) ga
|
||||||
|
|
||||||
// If value is transferred, it is charged before 1/64th
|
// If value is transferred, it is charged before 1/64th
|
||||||
// is subtracted from the available gas pool.
|
// is subtracted from the available gas pool.
|
||||||
if withTransferCosts && !stack.Back(2).IsZero() {
|
if withTransferCosts && !stack.back(2).IsZero() {
|
||||||
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.RegularGas)
|
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas.RegularGas)
|
||||||
if wantedValueTransferWitnessGas > contract.Gas.RegularGas {
|
if wantedValueTransferWitnessGas > contract.Gas.RegularGas {
|
||||||
return GasCosts{RegularGas: wantedValueTransferWitnessGas}, nil
|
return GasCosts{RegularGas: wantedValueTransferWitnessGas}, nil
|
||||||
|
|
@ -168,8 +168,8 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
||||||
gas := gasCost.RegularGas
|
gas := gasCost.RegularGas
|
||||||
if !contract.IsDeployment && !contract.IsSystemCall {
|
if !contract.IsDeployment && !contract.IsSystemCall {
|
||||||
var (
|
var (
|
||||||
codeOffset = stack.Back(1)
|
codeOffset = stack.back(1)
|
||||||
length = stack.Back(2)
|
length = stack.back(2)
|
||||||
)
|
)
|
||||||
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||||
if overflow {
|
if overflow {
|
||||||
|
|
|
||||||
|
|
@ -959,3 +959,63 @@ func TestDelegatedAccountAccessCost(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestManyLargeStacks(t *testing.T) {
|
||||||
|
// This piece of code will push 512 items to the stack, and then call itself
|
||||||
|
// recursively.
|
||||||
|
code := make([]byte, 10)
|
||||||
|
for i := range code {
|
||||||
|
code[i] = byte(vm.PUSH0)
|
||||||
|
}
|
||||||
|
code = append(code, []byte{
|
||||||
|
byte(vm.ADDRESS), // address to call
|
||||||
|
byte(vm.GAS),
|
||||||
|
byte(vm.CALL),
|
||||||
|
}...)
|
||||||
|
|
||||||
|
main := common.HexToAddress("0xbb")
|
||||||
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
|
statedb.SetCode(main, code, tracing.CodeChangeUnspecified)
|
||||||
|
|
||||||
|
//tracer := logger.NewJSONLogger(nil, os.Stdout)
|
||||||
|
var tracer *tracing.Hooks
|
||||||
|
_, _, err := Call(main, nil, &Config{
|
||||||
|
GasLimit: 10_000_000,
|
||||||
|
State: statedb,
|
||||||
|
EVMConfig: vm.Config{
|
||||||
|
Tracer: tracer,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("didn't expect error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkLargeDeepStacks(b *testing.B) {
|
||||||
|
// This piece of code will push 512 items to the stack, and then call itself
|
||||||
|
// recursively.
|
||||||
|
code := make([]byte, 512)
|
||||||
|
for i := range code {
|
||||||
|
code[i] = byte(vm.PUSH0)
|
||||||
|
}
|
||||||
|
code = append(code, []byte{
|
||||||
|
byte(vm.ADDRESS), // address to call
|
||||||
|
byte(vm.GAS),
|
||||||
|
byte(vm.CALL),
|
||||||
|
}...)
|
||||||
|
benchmarkNonModifyingCode(10_000_000, code, "deep-large-stacks-10M", "", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkShortDeepStacks(b *testing.B) {
|
||||||
|
// This piece of code will push a few items to the stack, and then call itself
|
||||||
|
// recursively.
|
||||||
|
code := make([]byte, 8)
|
||||||
|
for i := range code {
|
||||||
|
code[i] = byte(vm.PUSH0)
|
||||||
|
}
|
||||||
|
code = append(code, []byte{
|
||||||
|
byte(vm.ADDRESS), // address to call
|
||||||
|
byte(vm.GAS),
|
||||||
|
byte(vm.CALL),
|
||||||
|
}...)
|
||||||
|
benchmarkNonModifyingCode(10_000_000, code, "deep-short-stacks-10M", "", b)
|
||||||
|
}
|
||||||
|
|
|
||||||
209
core/vm/stack.go
209
core/vm/stack.go
|
|
@ -17,111 +17,170 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// stackArena is an arena which actual evm stacks use for data storage
|
||||||
|
type stackArena struct {
|
||||||
|
data []uint256.Int
|
||||||
|
top int // first free slot
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArena() *stackArena {
|
||||||
|
return stackPool.Get().(*stackArena)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1025, because in stack() there is a condition check
|
||||||
|
// for the stack size that would fail if it was set to
|
||||||
|
// 1024.
|
||||||
|
const initialStackSize = 1025
|
||||||
|
|
||||||
var stackPool = sync.Pool{
|
var stackPool = sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() any {
|
||||||
return &Stack{data: make([]uint256.Int, 0, 16)}
|
return &stackArena{
|
||||||
|
data: make([]uint256.Int, initialStackSize),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func returnStack(arena *stackArena) {
|
||||||
|
arena.top = 0 // defensive, not strictly needed as s.inner.top = s.bottom in release()
|
||||||
|
stackPool.Put(arena)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stack returns an instance of a stack which uses the underlying arena. The instance
|
||||||
|
// must be released by invoking the (*Stack).release() method
|
||||||
|
func (sa *stackArena) stack() *Stack {
|
||||||
|
// make sure every substack has at least 1024 elements
|
||||||
|
if len(sa.data) <= sa.top+1024 {
|
||||||
|
// we need to grow the arena
|
||||||
|
sa.data = slices.Grow(sa.data, 1024)
|
||||||
|
sa.data = sa.data[:cap(sa.data)]
|
||||||
|
}
|
||||||
|
return &Stack{
|
||||||
|
bottom: sa.top,
|
||||||
|
size: 0,
|
||||||
|
inner: sa,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newStackForTesting is meant to be used solely for testing. It creates a stack
|
||||||
|
// backed by a newly allocated arena.
|
||||||
|
func newStackForTesting() *Stack {
|
||||||
|
arena := &stackArena{
|
||||||
|
data: make([]uint256.Int, 1025),
|
||||||
|
}
|
||||||
|
return arena.stack()
|
||||||
|
}
|
||||||
|
|
||||||
// Stack is an object for basic stack operations. Items popped to the stack are
|
// Stack is an object for basic stack operations. Items popped to the stack are
|
||||||
// expected to be changed and modified. stack does not take care of adding newly
|
// expected to be changed and modified. stack does not take care of adding newly
|
||||||
// initialized objects.
|
// initialized objects.
|
||||||
type Stack struct {
|
type Stack struct {
|
||||||
data []uint256.Int
|
bottom int // bottom is the index of the first element of this stack
|
||||||
|
size int // size is the number of elements in this stack
|
||||||
|
inner *stackArena
|
||||||
}
|
}
|
||||||
|
|
||||||
func newstack() *Stack {
|
// release un-claims the area of the arena which was claimed by the stack.
|
||||||
return stackPool.Get().(*Stack)
|
func (s *Stack) release() {
|
||||||
}
|
// When the stack is returned, need to notify the arena that the new 'top' is
|
||||||
|
// the returned stack's bottom.
|
||||||
func returnStack(s *Stack) {
|
s.inner.top = s.bottom
|
||||||
s.data = s.data[:0]
|
|
||||||
stackPool.Put(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data returns the underlying uint256.Int array.
|
// Data returns the underlying uint256.Int array.
|
||||||
func (st *Stack) Data() []uint256.Int {
|
func (s *Stack) Data() []uint256.Int {
|
||||||
return st.data
|
return s.inner.data[s.bottom : s.bottom+s.size]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) push(d *uint256.Int) {
|
func (s *Stack) push(d *uint256.Int) {
|
||||||
// NOTE push limit (1024) is checked in baseCheck
|
elem := s.get()
|
||||||
st.data = append(st.data, *d)
|
*elem = *d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) pop() (ret uint256.Int) {
|
// get returns a pointer to a newly created element
|
||||||
ret = st.data[len(st.data)-1]
|
// on top of the stack
|
||||||
st.data = st.data[:len(st.data)-1]
|
func (s *Stack) get() *uint256.Int {
|
||||||
return
|
elem := &s.inner.data[s.inner.top]
|
||||||
|
s.inner.top++
|
||||||
|
s.size++
|
||||||
|
return elem
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) len() int {
|
func (s *Stack) pop() uint256.Int {
|
||||||
return len(st.data)
|
s.inner.top--
|
||||||
|
s.size--
|
||||||
|
return s.inner.data[s.inner.top]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) swap1() {
|
func (s *Stack) len() int {
|
||||||
st.data[st.len()-2], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-2]
|
return s.size
|
||||||
}
|
|
||||||
func (st *Stack) swap2() {
|
|
||||||
st.data[st.len()-3], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-3]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap3() {
|
|
||||||
st.data[st.len()-4], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-4]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap4() {
|
|
||||||
st.data[st.len()-5], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-5]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap5() {
|
|
||||||
st.data[st.len()-6], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-6]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap6() {
|
|
||||||
st.data[st.len()-7], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-7]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap7() {
|
|
||||||
st.data[st.len()-8], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-8]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap8() {
|
|
||||||
st.data[st.len()-9], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-9]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap9() {
|
|
||||||
st.data[st.len()-10], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-10]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap10() {
|
|
||||||
st.data[st.len()-11], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-11]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap11() {
|
|
||||||
st.data[st.len()-12], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-12]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap12() {
|
|
||||||
st.data[st.len()-13], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-13]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap13() {
|
|
||||||
st.data[st.len()-14], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-14]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap14() {
|
|
||||||
st.data[st.len()-15], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-15]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap15() {
|
|
||||||
st.data[st.len()-16], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-16]
|
|
||||||
}
|
|
||||||
func (st *Stack) swap16() {
|
|
||||||
st.data[st.len()-17], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-17]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) dup(n int) {
|
func (s *Stack) swap1() {
|
||||||
st.push(&st.data[st.len()-n])
|
s.inner.data[s.bottom+s.size-2], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-2]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap2() {
|
||||||
|
s.inner.data[s.bottom+s.size-3], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-3]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap3() {
|
||||||
|
s.inner.data[s.bottom+s.size-4], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-4]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap4() {
|
||||||
|
s.inner.data[s.bottom+s.size-5], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-5]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap5() {
|
||||||
|
s.inner.data[s.bottom+s.size-6], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-6]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap6() {
|
||||||
|
s.inner.data[s.bottom+s.size-7], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-7]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap7() {
|
||||||
|
s.inner.data[s.bottom+s.size-8], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-8]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap8() {
|
||||||
|
s.inner.data[s.bottom+s.size-9], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-9]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap9() {
|
||||||
|
s.inner.data[s.bottom+s.size-10], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-10]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap10() {
|
||||||
|
s.inner.data[s.bottom+s.size-11], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-11]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap11() {
|
||||||
|
s.inner.data[s.bottom+s.size-12], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-12]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap12() {
|
||||||
|
s.inner.data[s.bottom+s.size-13], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-13]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap13() {
|
||||||
|
s.inner.data[s.bottom+s.size-14], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-14]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap14() {
|
||||||
|
s.inner.data[s.bottom+s.size-15], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-15]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap15() {
|
||||||
|
s.inner.data[s.bottom+s.size-16], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-16]
|
||||||
|
}
|
||||||
|
func (s *Stack) swap16() {
|
||||||
|
s.inner.data[s.bottom+s.size-17], s.inner.data[s.bottom+s.size-1] = s.inner.data[s.bottom+s.size-1], s.inner.data[s.bottom+s.size-17]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Stack) peek() *uint256.Int {
|
func (s *Stack) dup(n int) {
|
||||||
return &st.data[st.len()-1]
|
s.inner.data[s.bottom+s.size] = s.inner.data[s.bottom+s.size-n]
|
||||||
|
s.size++
|
||||||
|
s.inner.top++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Back returns the n'th item in stack
|
func (s *Stack) peek() *uint256.Int {
|
||||||
func (st *Stack) Back(n int) *uint256.Int {
|
return &s.inner.data[s.bottom+s.size-1]
|
||||||
return &st.data[st.len()-n-1]
|
}
|
||||||
|
|
||||||
|
// back returns the n'th item in stack
|
||||||
|
func (s *Stack) back(n int) *uint256.Int {
|
||||||
|
return &s.inner.data[s.bottom+s.size-n-1]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,10 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cap the maximum gas allowance according to EIP-7825 if the estimation targets Osaka
|
// Cap the maximum gas allowance according to EIP-7825 if the estimation targets Osaka
|
||||||
if hi > params.MaxTxGas {
|
isOsaka := opts.Config.IsOsaka(opts.Header.Number, opts.Header.Time)
|
||||||
if opts.Config.IsOsaka(opts.Header.Number, opts.Header.Time) {
|
isAmsterdam := opts.Config.IsAmsterdam(opts.Header.Number, opts.Header.Time)
|
||||||
hi = params.MaxTxGas
|
if hi > params.MaxTxGas && isOsaka && !isAmsterdam {
|
||||||
}
|
hi = params.MaxTxGas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize the max fee per gas the call is willing to spend.
|
// Normalize the max fee per gas the call is willing to spend.
|
||||||
|
|
@ -244,6 +244,7 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio
|
||||||
evmContext.BlobBaseFee = new(big.Int)
|
evmContext.BlobBaseFee = new(big.Int)
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(evmContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true})
|
evm := vm.NewEVM(evmContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true})
|
||||||
|
defer evm.Release()
|
||||||
|
|
||||||
// Monitor the outer context and interrupt the EVM upon cancellation. To avoid
|
// Monitor the outer context and interrupt the EVM upon cancellation. To avoid
|
||||||
// a dangling goroutine until the outer estimation finishes, create an internal
|
// a dangling goroutine until the outer estimation finishes, create an internal
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
|
||||||
// Insert parent beacon block root in the state as per EIP-4788.
|
// Insert parent beacon block root in the state as per EIP-4788.
|
||||||
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
|
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
|
||||||
evm := vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{})
|
evm := vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{})
|
||||||
|
defer evm.Release()
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -379,6 +379,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
|
||||||
if api.backend.ChainConfig().IsPrague(next.Number(), next.Time()) {
|
if api.backend.ChainConfig().IsPrague(next.Number(), next.Time()) {
|
||||||
core.ProcessParentBlockHash(next.ParentHash(), evm)
|
core.ProcessParentBlockHash(next.ParentHash(), evm)
|
||||||
}
|
}
|
||||||
|
evm.Release()
|
||||||
// Clean out any pending release functions of trace state. Note this
|
// Clean out any pending release functions of trace state. Note this
|
||||||
// step must be done after constructing tracing state, because the
|
// step must be done after constructing tracing state, because the
|
||||||
// tracing state of block next depends on the parent state and construction
|
// tracing state of block next depends on the parent state and construction
|
||||||
|
|
@ -524,6 +525,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
|
||||||
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
|
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
|
||||||
)
|
)
|
||||||
evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{})
|
evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{})
|
||||||
|
defer evm.Release()
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
}
|
}
|
||||||
|
|
@ -584,6 +586,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
|
|
||||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||||
evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
|
evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
|
defer evm.Release()
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
}
|
}
|
||||||
|
|
@ -673,6 +676,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
|
||||||
var failed error
|
var failed error
|
||||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||||
evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
|
evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
|
defer evm.Release()
|
||||||
|
|
||||||
txloop:
|
txloop:
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
|
|
@ -758,6 +762,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
}
|
}
|
||||||
|
|
||||||
evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{})
|
evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{})
|
||||||
|
defer evm.Release()
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
}
|
}
|
||||||
|
|
@ -805,6 +810,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
}
|
}
|
||||||
_, err = core.ApplyMessage(evm, msg, nil)
|
_, err = core.ApplyMessage(evm, msg, nil)
|
||||||
|
evm.Release()
|
||||||
if writer != nil {
|
if writer != nil {
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
}
|
}
|
||||||
|
|
@ -817,7 +823,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
}
|
}
|
||||||
// Finalize the state so any modifications are written to the trie
|
// Finalize the state so any modifications are written to the trie
|
||||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||||
statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number()))
|
statedb.Finalise(chainConfig.IsEIP158(block.Number()))
|
||||||
|
|
||||||
// If we've traced the transaction we were looking for, abort
|
// If we've traced the transaction we were looking for, abort
|
||||||
if tx.Hash() == txHash {
|
if tx.Hash() == txHash {
|
||||||
|
|
@ -999,6 +1005,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
||||||
}
|
}
|
||||||
tracingStateDB := state.NewHookedState(statedb, tracer.Hooks)
|
tracingStateDB := state.NewHookedState(statedb, tracer.Hooks)
|
||||||
evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
|
evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
|
||||||
|
defer evm.Release()
|
||||||
if precompiles != nil {
|
if precompiles != nil {
|
||||||
evm.SetPrecompiles(precompiles)
|
evm.SetPrecompiles(precompiles)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -775,6 +775,7 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
|
||||||
blockContext.BlobBaseFee = new(big.Int)
|
blockContext.BlobBaseFee = new(big.Int)
|
||||||
}
|
}
|
||||||
evm := b.GetEVM(ctx, state, header, vmConfig, blockContext)
|
evm := b.GetEVM(ctx, state, header, vmConfig, blockContext)
|
||||||
|
defer evm.Release()
|
||||||
if precompiles != nil {
|
if precompiles != nil {
|
||||||
evm.SetPrecompiles(precompiles)
|
evm.SetPrecompiles(precompiles)
|
||||||
}
|
}
|
||||||
|
|
@ -1390,6 +1391,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
evm.Context.BlobBaseFee = new(big.Int)
|
evm.Context.BlobBaseFee = new(big.Int)
|
||||||
}
|
}
|
||||||
res, err := core.ApplyMessage(evm, msg, nil)
|
res, err := core.ApplyMessage(evm, msg, nil)
|
||||||
|
evm.Release()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction(types.LegacyTxType).Hash(), err)
|
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction(types.LegacyTxType).Hash(), err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -312,6 +312,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
tracingStateDB = state.NewHookedState(sim.state, hooks)
|
tracingStateDB = state.NewHookedState(sim.state, hooks)
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(blockContext, tracingStateDB, sim.chainConfig, *vmConfig)
|
evm := vm.NewEVM(blockContext, tracingStateDB, sim.chainConfig, *vmConfig)
|
||||||
|
defer evm.Release()
|
||||||
// It is possible to override precompiles with EVM bytecode, or
|
// It is possible to override precompiles with EVM bytecode, or
|
||||||
// move them to another address.
|
// move them to another address.
|
||||||
if precompiles != nil {
|
if precompiles != nil {
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,9 @@ func (env *environment) txFitsSize(tx *types.Transaction) bool {
|
||||||
// discard terminates the background threads before discarding it.
|
// discard terminates the background threads before discarding it.
|
||||||
func (env *environment) discard() {
|
func (env *environment) discard() {
|
||||||
env.state.StopPrefetcher()
|
env.state.StopPrefetcher()
|
||||||
|
if env.evm != nil {
|
||||||
|
env.evm.Release()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -560,7 +563,7 @@ func (miner *Miner) fillTransactions(ctx context.Context, interrupt *atomic.Int3
|
||||||
if env.header.ExcessBlobGas != nil {
|
if env.header.ExcessBlobGas != nil {
|
||||||
filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(miner.chainConfig, env.header))
|
filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(miner.chainConfig, env.header))
|
||||||
}
|
}
|
||||||
if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) {
|
if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) && !miner.chainConfig.IsAmsterdam(env.header.Number, env.header.Time) {
|
||||||
filter.GasLimitCap = params.MaxTxGas
|
filter.GasLimitCap = params.MaxTxGas
|
||||||
}
|
}
|
||||||
filter.BlobTxs = false
|
filter.BlobTxs = false
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@
|
||||||
package discover
|
package discover
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"iter"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
@ -143,3 +145,16 @@ func (r *reseedingRandom) Shuffle(n int, swap func(i, j int)) {
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
r.cur.Shuffle(n, swap)
|
r.cur.Shuffle(n, swap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// iterList iterates over the elements of the given list.
|
||||||
|
func iterList[T any](l *list.List) iter.Seq2[T, *list.Element] {
|
||||||
|
return func(yield func(T, *list.Element) bool) {
|
||||||
|
for el := l.Front(); el != nil; {
|
||||||
|
next := el.Next()
|
||||||
|
if !yield(el.Value.(T), el) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
el = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -446,9 +446,8 @@ func (t *UDPv4) loop() {
|
||||||
}
|
}
|
||||||
// Start the timer so it fires when the next pending reply has expired.
|
// Start the timer so it fires when the next pending reply has expired.
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
for el := plist.Front(); el != nil; el = el.Next() {
|
for p, el := range iterList[*replyMatcher](plist) {
|
||||||
nextTimeout = el.Value.(*replyMatcher)
|
if dist := p.deadline.Sub(now); dist < 2*respTimeout {
|
||||||
if dist := nextTimeout.deadline.Sub(now); dist < 2*respTimeout {
|
|
||||||
timeout.Reset(dist)
|
timeout.Reset(dist)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -478,8 +477,7 @@ func (t *UDPv4) loop() {
|
||||||
|
|
||||||
case r := <-t.gotreply:
|
case r := <-t.gotreply:
|
||||||
var matched bool // whether any replyMatcher considered the reply acceptable.
|
var matched bool // whether any replyMatcher considered the reply acceptable.
|
||||||
for el := plist.Front(); el != nil; el = el.Next() {
|
for p, el := range iterList[*replyMatcher](plist) {
|
||||||
p := el.Value.(*replyMatcher)
|
|
||||||
if p.from == r.from && p.ptype == r.data.Kind() && p.ip == r.ip {
|
if p.from == r.from && p.ptype == r.data.Kind() && p.ip == r.ip {
|
||||||
ok, requestDone := p.callback(r.data)
|
ok, requestDone := p.callback(r.data)
|
||||||
matched = matched || ok
|
matched = matched || ok
|
||||||
|
|
@ -499,8 +497,7 @@ func (t *UDPv4) loop() {
|
||||||
nextTimeout = nil
|
nextTimeout = nil
|
||||||
|
|
||||||
// Notify and remove callbacks whose deadline is in the past.
|
// Notify and remove callbacks whose deadline is in the past.
|
||||||
for el := plist.Front(); el != nil; el = el.Next() {
|
for p, el := range iterList[*replyMatcher](plist) {
|
||||||
p := el.Value.(*replyMatcher)
|
|
||||||
if now.After(p.deadline) || now.Equal(p.deadline) {
|
if now.After(p.deadline) || now.Equal(p.deadline) {
|
||||||
p.errc <- errTimeout
|
p.errc <- errTimeout
|
||||||
plist.Remove(el)
|
plist.Remove(el)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue