mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-24 16:59:26 +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
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build (darwin && !ios && cgo) || freebsd || (linux && !arm64) || netbsd || solaris
|
||||
// +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris
|
||||
//go:build (darwin && !ios && cgo) || freebsd || linux || netbsd || solaris
|
||||
// +build darwin,!ios,cgo freebsd linux netbsd solaris
|
||||
|
||||
package keystore
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
// 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/>.
|
||||
|
||||
//go:build (darwin && !cgo) || ios || (linux && arm64) || windows || (!darwin && !freebsd && !linux && !netbsd && !solaris)
|
||||
// +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 windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
||||
|
||||
// This is the fallback implementation of directory watching.
|
||||
// It is used on unsupported platforms.
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ func (hub *Hub) readPairings() 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 {
|
||||
return err
|
||||
}
|
||||
|
|
@ -129,11 +129,8 @@ func (hub *Hub) writePairings() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if _, err := pairingFile.Write(pairingData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
_, err = pairingFile.Write(pairingData)
|
||||
return err
|
||||
}
|
||||
|
||||
func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@
|
|||
package t8ntool
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
stdmath "math"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
|
@ -47,6 +49,9 @@ type Prestate struct {
|
|||
Env stEnv `json:"env"`
|
||||
Pre types.GenesisAlloc `json:"pre"`
|
||||
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
|
||||
|
|
@ -146,8 +151,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
return h
|
||||
}
|
||||
var (
|
||||
isEIP4762 = chainConfig.IsUBT(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
|
||||
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762)
|
||||
isEIP4762 = chainConfig.IsUBT(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
|
||||
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)
|
||||
gaspool = core.NewGasPool(pre.Env.GasLimit)
|
||||
blockHash = common.Hash{0x13, 0x37}
|
||||
|
|
@ -414,6 +430,76 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool
|
|||
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) {
|
||||
hw := keccak.NewLegacyKeccak256()
|
||||
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")
|
||||
}
|
||||
results = append(results, r)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package t8ntool
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -115,11 +116,10 @@ func Transition(ctx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
if allocStr != stdinSelector {
|
||||
if err := readFile(allocStr, "alloc", &inputData.Alloc); err != nil {
|
||||
return err
|
||||
}
|
||||
prestate.AllocPath = allocStr
|
||||
} else {
|
||||
prestate.Pre = inputData.Alloc
|
||||
}
|
||||
prestate.Pre = inputData.Alloc
|
||||
|
||||
if btStr != stdinSelector && btStr != "" {
|
||||
if err := readFile(btStr, "BT", &inputData.BT); err != nil {
|
||||
|
|
@ -223,22 +223,57 @@ func Transition(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
// Dump the execution result
|
||||
// Dump the execution result.
|
||||
var (
|
||||
collector = make(Alloc)
|
||||
collector Alloc
|
||||
btleaves map[common.Hash]hexutil.Bytes
|
||||
)
|
||||
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)
|
||||
} else {
|
||||
default:
|
||||
btleaves = make(map[common.Hash]hexutil.Bytes)
|
||||
if err := s.DumpBinTrieLeaves(btleaves); err != nil {
|
||||
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 {
|
||||
|
|
@ -327,6 +362,10 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
|||
if addr == nil {
|
||||
return
|
||||
}
|
||||
g[*addr] = dumpAccountToTypesAccount(dumpAccount)
|
||||
}
|
||||
|
||||
func dumpAccountToTypesAccount(dumpAccount state.DumpAccount) types.Account {
|
||||
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
|
||||
var storage map[common.Hash]common.Hash
|
||||
if dumpAccount.Storage != nil {
|
||||
|
|
@ -335,13 +374,64 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
|||
storage[k] = common.HexToHash(v)
|
||||
}
|
||||
}
|
||||
genesisAccount := types.Account{
|
||||
return types.Account{
|
||||
Code: dumpAccount.Code,
|
||||
Storage: storage,
|
||||
Balance: balance,
|
||||
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
|
||||
|
|
@ -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
|
||||
// files
|
||||
func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes, bt map[common.Hash]hexutil.Bytes) error {
|
||||
// files. An empty allocOutput skips the alloc dispatch, which is used when the
|
||||
// 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{})
|
||||
stdErrObject := make(map[string]interface{})
|
||||
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
|
||||
}
|
||||
if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
|
||||
if err := dispatch(baseDir, allocOutput, "alloc", alloc); err != nil {
|
||||
return err
|
||||
}
|
||||
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.
|
||||
func (s *stateObject) GetState(key common.Hash) common.Hash {
|
||||
value, _ := s.getState(key)
|
||||
return value
|
||||
value, dirty := s.dirtyStorage[key]
|
||||
if dirty {
|
||||
return value
|
||||
}
|
||||
return s.GetCommittedState(key)
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
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.
|
||||
context = NewEVMBlockContext(header, p.chain, nil)
|
||||
evm := vm.NewEVM(context, tracingStateDB, config, cfg)
|
||||
defer evm.Release()
|
||||
|
||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||
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)
|
||||
isAmsterdam := st.evm.ChainConfig().IsAmsterdam(st.evm.Context.BlockNumber, st.evm.Context.Time)
|
||||
if !msg.SkipTransactionChecks {
|
||||
// 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)
|
||||
}
|
||||
// Make sure the sender is an EOA
|
||||
|
|
|
|||
|
|
@ -1222,8 +1222,10 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
|
|||
pool.mu.Lock()
|
||||
if reset != nil {
|
||||
if reset.newHead != nil && reset.oldHead != nil {
|
||||
// Discard the transactions with the gas limit higher than the cap.
|
||||
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) && !pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
|
||||
// Discard the transactions with the gas limit higher than the cap at the
|
||||
// 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
|
||||
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
|
||||
if tx.Gas() > params.MaxTxGas {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
|||
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())
|
||||
}
|
||||
// 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/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
var activators = map[int]func(*JumpTable){
|
||||
|
|
@ -92,8 +91,7 @@ func enable1884(jt *JumpTable) {
|
|||
}
|
||||
|
||||
func opSelfBalance(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
balance := evm.StateDB.GetBalance(scope.Contract.Address())
|
||||
scope.Stack.push(balance)
|
||||
scope.Stack.get().Set(evm.StateDB.GetBalance(scope.Contract.Address()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -111,8 +109,7 @@ func enable1344(jt *JumpTable) {
|
|||
|
||||
// opChainID implements CHAINID opcode
|
||||
func opChainID(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
chainId, _ := uint256.FromBig(evm.chainConfig.ChainID)
|
||||
scope.Stack.push(chainId)
|
||||
scope.Stack.get().SetFromBig(evm.chainConfig.ChainID)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -222,8 +219,7 @@ func opTstore(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
|
||||
// opBaseFee implements BASEFEE opcode
|
||||
func opBaseFee(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
baseFee, _ := uint256.FromBig(evm.Context.BaseFee)
|
||||
scope.Stack.push(baseFee)
|
||||
scope.Stack.get().SetFromBig(evm.Context.BaseFee)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -240,7 +236,7 @@ func enable3855(jt *JumpTable) {
|
|||
|
||||
// opPush0 implements the PUSH0 opcode
|
||||
func opPush0(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int))
|
||||
scope.Stack.get().Clear()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -291,8 +287,7 @@ func opBlobHash(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
|
||||
// opBlobBaseFee implements BLOBBASEFEE opcode
|
||||
func opBlobBaseFee(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
blobBaseFee, _ := uint256.FromBig(evm.Context.BlobBaseFee)
|
||||
scope.Stack.push(blobBaseFee)
|
||||
scope.Stack.get().SetFromBig(evm.Context.BlobBaseFee)
|
||||
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) {
|
||||
var (
|
||||
codeLen = uint64(len(scope.Contract.Code))
|
||||
integer = new(uint256.Int)
|
||||
elem = scope.Stack.get()
|
||||
)
|
||||
*pc += 1
|
||||
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 {
|
||||
// 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 {
|
||||
scope.Stack.push(integer.Clear())
|
||||
elem.Clear()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -426,12 +421,11 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
|||
start = min(codeLen, int(*pc+1))
|
||||
end = min(codeLen, start+pushByteSize)
|
||||
)
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(
|
||||
scope.Stack.get().SetBytes(
|
||||
common.RightPadBytes(
|
||||
scope.Contract.Code[start:end],
|
||||
pushByteSize,
|
||||
)),
|
||||
)
|
||||
))
|
||||
|
||||
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
|
||||
contractAddr := scope.Contract.Address()
|
||||
|
|
@ -583,7 +577,7 @@ func enable7702(jt *JumpTable) {
|
|||
|
||||
// opSlotNum enables the SLOTNUM opcode
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,8 @@ type EVM struct {
|
|||
|
||||
readOnly bool // Whether to throw on stateful modifications
|
||||
returnData []byte // Last CALL's return data for subsequent reuse
|
||||
|
||||
arena *stackArena
|
||||
}
|
||||
|
||||
// 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,
|
||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
|
||||
jumpDests: newGlobalJumpDests(),
|
||||
arena: newArena(),
|
||||
}
|
||||
evm.precompiles = activePrecompiledContracts(evm.chainRules)
|
||||
|
||||
|
|
@ -223,6 +226,12 @@ func (evm *EVM) Cancel() {
|
|||
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
|
||||
func (evm *EVM) Cancelled() bool {
|
||||
return evm.abort.Load()
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ func memoryCopierGas(stackpos int) gasFunc {
|
|||
return GasCosts{}, err
|
||||
}
|
||||
// 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 {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
|
|||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
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())
|
||||
)
|
||||
// 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
|
||||
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())
|
||||
)
|
||||
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 {
|
||||
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 {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
|
@ -261,7 +261,7 @@ func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
|
|||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
|
||||
wordGas, overflow := stack.back(1).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
|
@ -299,7 +299,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
|
|||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
wordGas, overflow := stack.back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
|
@ -317,7 +317,7 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
size, overflow := stack.back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
return GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
|
|
@ -336,7 +336,7 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
|||
if err != nil {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
size, overflow := stack.back(2).Uint64WithOverflow()
|
||||
if overflow {
|
||||
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) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
expByteLen := uint64((stack.back(1).BitLen() + 7) / 8)
|
||||
|
||||
var (
|
||||
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) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
expByteLen := uint64((stack.back(1).BitLen() + 7) / 8)
|
||||
|
||||
var (
|
||||
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 {
|
||||
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 {
|
||||
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) {
|
||||
var (
|
||||
gas uint64
|
||||
transfersValue = !stack.Back(2).IsZero()
|
||||
address = common.Address(stack.Back(1).Bytes20())
|
||||
transfersValue = !stack.back(2).IsZero()
|
||||
address = common.Address(stack.back(1).Bytes20())
|
||||
)
|
||||
if evm.readOnly && transfersValue {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
|
|
@ -453,7 +453,7 @@ func gasCallCodeIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memor
|
|||
gas uint64
|
||||
overflow bool
|
||||
)
|
||||
if stack.Back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 {
|
||||
if stack.back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 {
|
||||
gas += params.CallValueTransferGas
|
||||
}
|
||||
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:
|
||||
if evm.chainRules.IsEIP150 {
|
||||
gas = params.SelfdestructGasEIP150
|
||||
var address = common.Address(stack.Back(0).Bytes20())
|
||||
var address = common.Address(stack.back(0).Bytes20())
|
||||
|
||||
if evm.chainRules.IsEIP158 {
|
||||
// if empty and transfers value
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
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) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
|
||||
scope.Stack.get().SetBytes(scope.Contract.Address().Bytes())
|
||||
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) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(evm.Origin.Bytes()))
|
||||
scope.Stack.get().SetBytes(evm.Origin.Bytes())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +281,7 @@ func opCallDataLoad(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
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +304,7 @@ func opCallDataCopy(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
|
||||
}
|
||||
|
||||
|
|
@ -338,7 +337,7 @@ func opExtCodeSize(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
|
||||
}
|
||||
|
||||
|
|
@ -416,7 +415,7 @@ func opExtCodeHash(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
|
||||
}
|
||||
|
||||
|
|
@ -451,35 +450,32 @@ func opBlockhash(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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func opNumber(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(evm.Context.BlockNumber)
|
||||
scope.Stack.push(v)
|
||||
scope.Stack.get().SetFromBig(evm.Context.BlockNumber)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDifficulty(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
v, _ := uint256.FromBig(evm.Context.Difficulty)
|
||||
scope.Stack.push(v)
|
||||
scope.Stack.get().SetFromBig(evm.Context.Difficulty)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opRandom(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
v := new(uint256.Int).SetBytes(evm.Context.Random.Bytes())
|
||||
scope.Stack.push(v)
|
||||
scope.Stack.get().SetBytes(evm.Context.Random.Bytes())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -556,17 +552,17 @@ func opJumpdest(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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -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.
|
||||
scope.Stack.push(scope.Stack.Back(n - 1))
|
||||
scope.Stack.push(scope.Stack.back(n - 1))
|
||||
*pc += 1
|
||||
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}
|
||||
}
|
||||
|
||||
// The (n+1)‘th stack item is swapped with the top of the stack.
|
||||
indexTop := scope.Stack.len() - 1
|
||||
indexN := scope.Stack.len() - 1 - n
|
||||
scope.Stack.data[indexTop], scope.Stack.data[indexN] = scope.Stack.data[indexN], scope.Stack.data[indexTop]
|
||||
// The (n+1)’th stack item is swapped with the top of the stack.
|
||||
top := scope.Stack.peek()
|
||||
nth := scope.Stack.back(n)
|
||||
*top, *nth = *nth, *top
|
||||
*pc += 1
|
||||
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}
|
||||
}
|
||||
|
||||
// The (n+1)‘th stack item is swapped with the (m+1)‘th stack item.
|
||||
indexN := scope.Stack.len() - 1 - n
|
||||
indexM := scope.Stack.len() - 1 - m
|
||||
scope.Stack.data[indexN], scope.Stack.data[indexM] = scope.Stack.data[indexM], scope.Stack.data[indexN]
|
||||
// The (n+1)’th stack item is swapped with the (m+1)’th stack item.
|
||||
nth := scope.Stack.back(n)
|
||||
mth := scope.Stack.back(m)
|
||||
*nth, *mth = *mth, *nth
|
||||
*pc += 1
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -1106,13 +1102,13 @@ func makeLog(size int) executionFunc {
|
|||
func opPush1(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
codeLen = uint64(len(scope.Contract.Code))
|
||||
integer = new(uint256.Int)
|
||||
elem = scope.Stack.get()
|
||||
)
|
||||
*pc += 1
|
||||
if *pc < codeLen {
|
||||
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
||||
elem.SetUint64(uint64(scope.Contract.Code[*pc]))
|
||||
} else {
|
||||
scope.Stack.push(integer.Clear())
|
||||
elem.Clear()
|
||||
}
|
||||
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) {
|
||||
var (
|
||||
codeLen = uint64(len(scope.Contract.Code))
|
||||
integer = new(uint256.Int)
|
||||
elem = scope.Stack.get()
|
||||
)
|
||||
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 {
|
||||
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8))
|
||||
elem.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8)
|
||||
} else {
|
||||
scope.Stack.push(integer.Clear())
|
||||
elem.Clear()
|
||||
}
|
||||
*pc += 2
|
||||
return nil, nil
|
||||
|
|
@ -1142,13 +1138,13 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
|||
start = min(codeLen, int(*pc+1))
|
||||
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)
|
||||
if missing := pushByteSize - (end - start); missing > 0 {
|
||||
a.Lsh(a, uint(8*missing))
|
||||
}
|
||||
scope.Stack.push(a)
|
||||
*pc += size
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
|
|
@ -98,7 +99,7 @@ func init() {
|
|||
func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
pc = uint64(0)
|
||||
)
|
||||
|
||||
|
|
@ -109,8 +110,8 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
|
|||
stack.push(x)
|
||||
stack.push(y)
|
||||
opFn(&pc, evm, &ScopeContext{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
|
||||
if stack.len() != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", name, stack.len())
|
||||
}
|
||||
actual := stack.pop()
|
||||
|
||||
|
|
@ -196,7 +197,7 @@ func TestSAR(t *testing.T) {
|
|||
func TestAddMod(t *testing.T) {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
pc = uint64(0)
|
||||
)
|
||||
tests := []struct {
|
||||
|
|
@ -239,7 +240,7 @@ func TestWriteExpectedValues(t *testing.T) {
|
|||
getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
pc = uint64(0)
|
||||
)
|
||||
result := make([]TwoOperandTestcase, len(args))
|
||||
|
|
@ -282,23 +283,40 @@ func TestJsonTestcases(t *testing.T) {
|
|||
|
||||
func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
scope = &ScopeContext{nil, stack, nil}
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newStackForTesting()
|
||||
code = []byte{}
|
||||
opPush32 = makePush(32, 32)
|
||||
)
|
||||
// convert args
|
||||
intArgs := make([]*uint256.Int, len(args))
|
||||
for i, arg := range args {
|
||||
code = append(code, common.LeftPadBytes(common.Hex2Bytes(arg), 32)...)
|
||||
intArgs[i] = new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
|
||||
}
|
||||
pc := uint64(0)
|
||||
for bench.Loop() {
|
||||
for _, arg := range intArgs {
|
||||
stack.push(arg)
|
||||
scope := &ScopeContext{nil, stack, &Contract{Code: code}}
|
||||
start := time.Now()
|
||||
bench.ResetTimer()
|
||||
for i := 0; i < bench.N; i++ {
|
||||
for range len(args) {
|
||||
opPush32(&pc, evm, scope)
|
||||
pc += 32
|
||||
}
|
||||
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 {
|
||||
want := new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
|
||||
|
|
@ -519,7 +537,7 @@ func BenchmarkOpIsZero(b *testing.B) {
|
|||
func TestOpMstore(t *testing.T) {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
mem = NewMemory()
|
||||
)
|
||||
mem.Resize(64)
|
||||
|
|
@ -542,7 +560,7 @@ func TestOpMstore(t *testing.T) {
|
|||
func BenchmarkOpMstore(bench *testing.B) {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
mem = NewMemory()
|
||||
)
|
||||
mem.Resize(64)
|
||||
|
|
@ -561,7 +579,7 @@ func TestOpTstore(t *testing.T) {
|
|||
var (
|
||||
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
evm = NewEVM(BlockContext{}, statedb, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
mem = NewMemory()
|
||||
caller = common.Address{}
|
||||
to = common.Address{1}
|
||||
|
|
@ -600,7 +618,7 @@ func TestOpTstore(t *testing.T) {
|
|||
func BenchmarkOpKeccak256(bench *testing.B) {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
mem = NewMemory()
|
||||
)
|
||||
mem.Resize(32)
|
||||
|
|
@ -672,7 +690,7 @@ func TestCreate2Addresses(t *testing.T) {
|
|||
codeHash := crypto.Keccak256(code)
|
||||
address := crypto.CreateAddress2(origin, salt, codeHash)
|
||||
/*
|
||||
stack := newstack()
|
||||
stack := newStackForTesting()
|
||||
// salt, but we don't need that for this test
|
||||
stack.push(big.NewInt(int64(len(code)))) //size
|
||||
stack.push(big.NewInt(0)) // memstart
|
||||
|
|
@ -701,12 +719,12 @@ func TestRandom(t *testing.T) {
|
|||
} {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{Random: &tt.random}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
pc = uint64(0)
|
||||
)
|
||||
opRandom(&pc, evm, &ScopeContext{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
||||
if have, want := stack.len(), 1; have != want {
|
||||
t.Errorf("test '%v': want %d item(s) on stack, have %d: ", tt.name, have, want)
|
||||
}
|
||||
actual := stack.pop()
|
||||
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes()))
|
||||
|
|
@ -741,14 +759,14 @@ func TestBlobHash(t *testing.T) {
|
|||
} {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
pc = uint64(0)
|
||||
)
|
||||
evm.SetTxContext(TxContext{BlobHashes: tt.hashes})
|
||||
stack.push(uint256.NewInt(tt.idx))
|
||||
opBlobHash(&pc, evm, &ScopeContext{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
|
||||
if have, want := stack.len(), 1; have != want {
|
||||
t.Errorf("test '%v': want %d item(s) on stack, have %d: ", tt.name, have, want)
|
||||
}
|
||||
actual := stack.pop()
|
||||
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.expect.Bytes()))
|
||||
|
|
@ -844,7 +862,7 @@ func TestOpMCopy(t *testing.T) {
|
|||
} {
|
||||
var (
|
||||
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
pc = uint64(0)
|
||||
)
|
||||
data := common.FromHex(strings.ReplaceAll(tc.pre, " ", ""))
|
||||
|
|
@ -907,7 +925,7 @@ func TestPush(t *testing.T) {
|
|||
|
||||
scope := &ScopeContext{
|
||||
Memory: nil,
|
||||
Stack: newstack(),
|
||||
Stack: newStackForTesting(),
|
||||
Contract: &Contract{
|
||||
Code: code,
|
||||
},
|
||||
|
|
@ -988,7 +1006,7 @@ func TestOpCLZ(t *testing.T) {
|
|||
}
|
||||
for _, tc := range tests {
|
||||
// prepare a fresh stack and PC
|
||||
stack := newstack()
|
||||
stack := newStackForTesting()
|
||||
pc := uint64(0)
|
||||
|
||||
// parse input
|
||||
|
|
@ -1111,7 +1129,7 @@ func TestEIP8024_Execution(t *testing.T) {
|
|||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
code := common.FromHex(tc.codeHex)
|
||||
stack := newstack()
|
||||
stack := newStackForTesting()
|
||||
pc := uint64(0)
|
||||
scope := &ScopeContext{Stack: stack, Contract: &Contract{Code: code}}
|
||||
var err error
|
||||
|
|
@ -1189,8 +1207,9 @@ func TestEIP8024_Execution(t *testing.T) {
|
|||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
got := make([]uint64, 0, stack.len())
|
||||
for i := stack.len() - 1; i >= 0; i-- {
|
||||
got = append(got, stack.data[i].Uint64())
|
||||
data := stack.Data()
|
||||
for i := len(data) - 1; i >= 0; i-- {
|
||||
got = append(got, data[i].Uint64())
|
||||
}
|
||||
if 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 (
|
||||
op OpCode // current opcode
|
||||
jumpTable *JumpTable = evm.table
|
||||
mem = NewMemory() // bound memory
|
||||
stack = newstack() // local stack
|
||||
mem = NewMemory() // bound memory
|
||||
stack = evm.arena.stack() // local stack
|
||||
callContext = &ScopeContext{
|
||||
Memory: mem,
|
||||
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
|
||||
// they are returned to the pools
|
||||
defer func() {
|
||||
returnStack(stack)
|
||||
stack.release()
|
||||
mem.Free()
|
||||
}()
|
||||
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{})
|
||||
startGas uint64 = 100_000_000
|
||||
value = uint256.NewInt(0)
|
||||
stack = newstack()
|
||||
stack = newStackForTesting()
|
||||
mem = NewMemory()
|
||||
contract = NewContract(common.Address{}, common.Address{}, value, NewGasBudget(startGas), nil)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,59 +17,59 @@
|
|||
package vm
|
||||
|
||||
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) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||
return calcMemSize64(stack.back(0), stack.back(2))
|
||||
}
|
||||
|
||||
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) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(2))
|
||||
return calcMemSize64(stack.back(0), stack.back(2))
|
||||
}
|
||||
|
||||
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) {
|
||||
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||
return calcMemSize64WithUint(stack.back(0), 32)
|
||||
}
|
||||
|
||||
func memoryMStore8(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64WithUint(stack.Back(0), 1)
|
||||
return calcMemSize64WithUint(stack.back(0), 1)
|
||||
}
|
||||
|
||||
func memoryMStore(stack *Stack) (uint64, bool) {
|
||||
return calcMemSize64WithUint(stack.Back(0), 32)
|
||||
return calcMemSize64WithUint(stack.back(0), 32)
|
||||
}
|
||||
|
||||
func memoryMcopy(stack *Stack) (uint64, bool) {
|
||||
mStart := stack.Back(0) // stack[0]: dest
|
||||
if stack.Back(1).Gt(mStart) {
|
||||
mStart = stack.Back(1) // stack[1]: source
|
||||
mStart := stack.back(0) // stack[0]: dest
|
||||
if stack.back(1).Gt(mStart) {
|
||||
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) {
|
||||
return calcMemSize64(stack.Back(1), stack.Back(2))
|
||||
return calcMemSize64(stack.back(1), stack.back(2))
|
||||
}
|
||||
|
||||
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) {
|
||||
x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
|
||||
x, overflow := calcMemSize64(stack.back(5), stack.back(6))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
|
||||
y, overflow := calcMemSize64(stack.back(3), stack.back(4))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
|
|
@ -80,11 +80,11 @@ func memoryCall(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 {
|
||||
return 0, true
|
||||
}
|
||||
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
||||
y, overflow := calcMemSize64(stack.back(2), stack.back(3))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
|
|
@ -95,11 +95,11 @@ func memoryDelegateCall(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 {
|
||||
return 0, true
|
||||
}
|
||||
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
|
||||
y, overflow := calcMemSize64(stack.back(2), stack.back(3))
|
||||
if overflow {
|
||||
return 0, true
|
||||
}
|
||||
|
|
@ -110,13 +110,13 @@ func memoryStaticCall(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) {
|
||||
return calcMemSize64(stack.Back(0), stack.Back(1))
|
||||
return calcMemSize64(stack.back(0), stack.back(1))
|
||||
}
|
||||
|
||||
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
|
||||
var (
|
||||
y, x = stack.Back(1), stack.peek()
|
||||
y, x = stack.back(1), stack.peek()
|
||||
slot = common.Hash(x.Bytes32())
|
||||
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
|
||||
cost = uint64(0)
|
||||
|
|
@ -158,7 +158,7 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
|||
|
||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
|
||||
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
|
||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
||||
// 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
|
||||
// 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.
|
||||
transfersValue := !stack.Back(2).IsZero()
|
||||
transfersValue := !stack.back(2).IsZero()
|
||||
if evm.readOnly && transfersValue {
|
||||
return GasCosts{}, ErrWriteProtection
|
||||
}
|
||||
|
|
@ -281,7 +281,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc gasFunc) gasFunc {
|
|||
var (
|
||||
eip2929Cost 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
|
||||
// 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
|
||||
// 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 {
|
||||
return GasCosts{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
|||
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (GasCosts, error) {
|
||||
var (
|
||||
target = common.Address(stack.Back(1).Bytes20())
|
||||
target = common.Address(stack.back(1).Bytes20())
|
||||
witnessGas uint64
|
||||
_, isPrecompile = evm.precompile(target)
|
||||
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
|
||||
// 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)
|
||||
if wantedValueTransferWitnessGas > contract.Gas.RegularGas {
|
||||
return GasCosts{RegularGas: wantedValueTransferWitnessGas}, nil
|
||||
|
|
@ -168,8 +168,8 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
|
|||
gas := gasCost.RegularGas
|
||||
if !contract.IsDeployment && !contract.IsSystemCall {
|
||||
var (
|
||||
codeOffset = stack.Back(1)
|
||||
length = stack.Back(2)
|
||||
codeOffset = stack.back(1)
|
||||
length = stack.back(2)
|
||||
)
|
||||
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||
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
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"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{
|
||||
New: func() interface{} {
|
||||
return &Stack{data: make([]uint256.Int, 0, 16)}
|
||||
New: func() any {
|
||||
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
|
||||
// expected to be changed and modified. stack does not take care of adding newly
|
||||
// initialized objects.
|
||||
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 {
|
||||
return stackPool.Get().(*Stack)
|
||||
}
|
||||
|
||||
func returnStack(s *Stack) {
|
||||
s.data = s.data[:0]
|
||||
stackPool.Put(s)
|
||||
// release un-claims the area of the arena which was claimed by the 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.
|
||||
s.inner.top = s.bottom
|
||||
}
|
||||
|
||||
// Data returns the underlying uint256.Int array.
|
||||
func (st *Stack) Data() []uint256.Int {
|
||||
return st.data
|
||||
func (s *Stack) Data() []uint256.Int {
|
||||
return s.inner.data[s.bottom : s.bottom+s.size]
|
||||
}
|
||||
|
||||
func (st *Stack) push(d *uint256.Int) {
|
||||
// NOTE push limit (1024) is checked in baseCheck
|
||||
st.data = append(st.data, *d)
|
||||
func (s *Stack) push(d *uint256.Int) {
|
||||
elem := s.get()
|
||||
*elem = *d
|
||||
}
|
||||
|
||||
func (st *Stack) pop() (ret uint256.Int) {
|
||||
ret = st.data[len(st.data)-1]
|
||||
st.data = st.data[:len(st.data)-1]
|
||||
return
|
||||
// get returns a pointer to a newly created element
|
||||
// on top of the stack
|
||||
func (s *Stack) get() *uint256.Int {
|
||||
elem := &s.inner.data[s.inner.top]
|
||||
s.inner.top++
|
||||
s.size++
|
||||
return elem
|
||||
}
|
||||
|
||||
func (st *Stack) len() int {
|
||||
return len(st.data)
|
||||
func (s *Stack) pop() uint256.Int {
|
||||
s.inner.top--
|
||||
s.size--
|
||||
return s.inner.data[s.inner.top]
|
||||
}
|
||||
|
||||
func (st *Stack) swap1() {
|
||||
st.data[st.len()-2], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-2]
|
||||
}
|
||||
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 (s *Stack) len() int {
|
||||
return s.size
|
||||
}
|
||||
|
||||
func (st *Stack) dup(n int) {
|
||||
st.push(&st.data[st.len()-n])
|
||||
func (s *Stack) swap1() {
|
||||
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 {
|
||||
return &st.data[st.len()-1]
|
||||
func (s *Stack) dup(n int) {
|
||||
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 (st *Stack) Back(n int) *uint256.Int {
|
||||
return &st.data[st.len()-n-1]
|
||||
func (s *Stack) peek() *uint256.Int {
|
||||
return &s.inner.data[s.bottom+s.size-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
|
||||
if hi > params.MaxTxGas {
|
||||
if opts.Config.IsOsaka(opts.Header.Number, opts.Header.Time) {
|
||||
hi = params.MaxTxGas
|
||||
}
|
||||
isOsaka := opts.Config.IsOsaka(opts.Header.Number, opts.Header.Time)
|
||||
isAmsterdam := opts.Config.IsAmsterdam(opts.Header.Number, opts.Header.Time)
|
||||
if hi > params.MaxTxGas && isOsaka && !isAmsterdam {
|
||||
hi = params.MaxTxGas
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
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
|
||||
// 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.
|
||||
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
|
||||
evm := vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{})
|
||||
defer evm.Release()
|
||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||
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()) {
|
||||
core.ProcessParentBlockHash(next.ParentHash(), evm)
|
||||
}
|
||||
evm.Release()
|
||||
// Clean out any pending release functions of trace state. Note this
|
||||
// step must be done after constructing tracing state, because the
|
||||
// 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())
|
||||
)
|
||||
evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{})
|
||||
defer evm.Release()
|
||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||
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)
|
||||
evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
|
||||
defer evm.Release()
|
||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||
}
|
||||
|
|
@ -673,6 +676,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
|
|||
var failed error
|
||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||
evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
|
||||
defer evm.Release()
|
||||
|
||||
txloop:
|
||||
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{})
|
||||
defer evm.Release()
|
||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||
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)
|
||||
}
|
||||
_, err = core.ApplyMessage(evm, msg, nil)
|
||||
evm.Release()
|
||||
if writer != nil {
|
||||
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
|
||||
// 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 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)
|
||||
evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
|
||||
defer evm.Release()
|
||||
if precompiles != nil {
|
||||
evm.SetPrecompiles(precompiles)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -775,6 +775,7 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
|
|||
blockContext.BlobBaseFee = new(big.Int)
|
||||
}
|
||||
evm := b.GetEVM(ctx, state, header, vmConfig, blockContext)
|
||||
defer evm.Release()
|
||||
if precompiles != nil {
|
||||
evm.SetPrecompiles(precompiles)
|
||||
}
|
||||
|
|
@ -1390,6 +1391,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
|||
evm.Context.BlobBaseFee = new(big.Int)
|
||||
}
|
||||
res, err := core.ApplyMessage(evm, msg, nil)
|
||||
evm.Release()
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
evm := vm.NewEVM(blockContext, tracingStateDB, sim.chainConfig, *vmConfig)
|
||||
defer evm.Release()
|
||||
// It is possible to override precompiles with EVM bytecode, or
|
||||
// move them to another address.
|
||||
if precompiles != nil {
|
||||
|
|
|
|||
|
|
@ -83,6 +83,9 @@ func (env *environment) txFitsSize(tx *types.Transaction) bool {
|
|||
// discard terminates the background threads before discarding it.
|
||||
func (env *environment) discard() {
|
||||
env.state.StopPrefetcher()
|
||||
if env.evm != nil {
|
||||
env.evm.Release()
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -560,7 +563,7 @@ func (miner *Miner) fillTransactions(ctx context.Context, interrupt *atomic.Int3
|
|||
if env.header.ExcessBlobGas != nil {
|
||||
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.BlobTxs = false
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@
|
|||
package discover
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"crypto/ecdsa"
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"iter"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
|
@ -143,3 +145,16 @@ func (r *reseedingRandom) Shuffle(n int, swap func(i, j int)) {
|
|||
defer r.mu.Unlock()
|
||||
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.
|
||||
now := time.Now()
|
||||
for el := plist.Front(); el != nil; el = el.Next() {
|
||||
nextTimeout = el.Value.(*replyMatcher)
|
||||
if dist := nextTimeout.deadline.Sub(now); dist < 2*respTimeout {
|
||||
for p, el := range iterList[*replyMatcher](plist) {
|
||||
if dist := p.deadline.Sub(now); dist < 2*respTimeout {
|
||||
timeout.Reset(dist)
|
||||
return
|
||||
}
|
||||
|
|
@ -478,8 +477,7 @@ func (t *UDPv4) loop() {
|
|||
|
||||
case r := <-t.gotreply:
|
||||
var matched bool // whether any replyMatcher considered the reply acceptable.
|
||||
for el := plist.Front(); el != nil; el = el.Next() {
|
||||
p := el.Value.(*replyMatcher)
|
||||
for p, el := range iterList[*replyMatcher](plist) {
|
||||
if p.from == r.from && p.ptype == r.data.Kind() && p.ip == r.ip {
|
||||
ok, requestDone := p.callback(r.data)
|
||||
matched = matched || ok
|
||||
|
|
@ -499,8 +497,7 @@ func (t *UDPv4) loop() {
|
|||
nextTimeout = nil
|
||||
|
||||
// Notify and remove callbacks whose deadline is in the past.
|
||||
for el := plist.Front(); el != nil; el = el.Next() {
|
||||
p := el.Value.(*replyMatcher)
|
||||
for p, el := range iterList[*replyMatcher](plist) {
|
||||
if now.After(p.deadline) || now.Equal(p.deadline) {
|
||||
p.errc <- errTimeout
|
||||
plist.Remove(el)
|
||||
|
|
|
|||
Loading…
Reference in a new issue