core: miner: reduce allocations in block building (#33375)

I recently went on a longer flight and started profiling the geth block
production pipeline.
This PR contains a bunch of individual fixes split into separate
commits.
I can drop some if necessary.


Benchmarking is not super easy, the benchmark I wrote is a bit
non-deterministic.
I will try to write a better benchmark later
```
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/miner
cpu: Intel(R) Core(TM) Ultra 7 155U
                │ /tmp/old.txt │          /tmp/new.txt          │
                │    sec/op    │   sec/op     vs base           │
BuildPayload-14    141.5µ ± 3%   146.0µ ± 6%  ~ (p=0.346 n=200)

                │ /tmp/old.txt │             /tmp/new.txt             │
                │     B/op     │     B/op      vs base                │
BuildPayload-14   188.2Ki ± 4%   177.4Ki ± 4%  -5.71% (p=0.018 n=200)

                │ /tmp/old.txt │            /tmp/new.txt             │
                │  allocs/op   │  allocs/op   vs base                │
BuildPayload-14    2.703k ± 4%   2.453k ± 5%  -9.25% (p=0.000 n=200)
```
This commit is contained in:
Marius van der Wijden 2026-02-03 08:19:16 +01:00 committed by GitHub
parent 6530945dcd
commit 16a6531ac2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 97 additions and 101 deletions

View file

@ -53,9 +53,9 @@ func (bc *BlobConfig) blobPrice(excessBlobGas uint64) *big.Int {
return new(big.Int).Mul(f, big.NewInt(params.BlobTxBlobGasPerBlob)) return new(big.Int).Mul(f, big.NewInt(params.BlobTxBlobGasPerBlob))
} }
func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig { func latestBlobConfig(cfg *params.ChainConfig, time uint64) (BlobConfig, error) {
if cfg.BlobScheduleConfig == nil { if cfg.BlobScheduleConfig == nil {
return nil return BlobConfig{}, errors.New("no blob config")
} }
var ( var (
london = cfg.LondonBlock london = cfg.LondonBlock
@ -80,14 +80,14 @@ func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig {
case cfg.IsCancun(london, time) && s.Cancun != nil: case cfg.IsCancun(london, time) && s.Cancun != nil:
bc = s.Cancun bc = s.Cancun
default: default:
return nil return BlobConfig{}, errors.New("no blob config")
} }
return &BlobConfig{ return BlobConfig{
Target: bc.Target, Target: bc.Target,
Max: bc.Max, Max: bc.Max,
UpdateFraction: bc.UpdateFraction, UpdateFraction: bc.UpdateFraction,
} }, nil
} }
// VerifyEIP4844Header verifies the presence of the excessBlobGas field and that // VerifyEIP4844Header verifies the presence of the excessBlobGas field and that
@ -98,8 +98,8 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
panic("bad header pair") panic("bad header pair")
} }
bcfg := latestBlobConfig(config, header.Time) bcfg, err := latestBlobConfig(config, header.Time)
if bcfg == nil { if err != nil {
panic("called before EIP-4844 is active") panic("called before EIP-4844 is active")
} }
@ -130,11 +130,14 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
// blobs on top of the excess blob gas. // blobs on top of the excess blob gas.
func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTimestamp uint64) uint64 { func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTimestamp uint64) uint64 {
isOsaka := config.IsOsaka(config.LondonBlock, headTimestamp) isOsaka := config.IsOsaka(config.LondonBlock, headTimestamp)
bcfg := latestBlobConfig(config, headTimestamp) bcfg, err := latestBlobConfig(config, headTimestamp)
if err != nil {
panic("calculating excess blob gas on nil blob config")
}
return calcExcessBlobGas(isOsaka, bcfg, parent) return calcExcessBlobGas(isOsaka, bcfg, parent)
} }
func calcExcessBlobGas(isOsaka bool, bcfg *BlobConfig, parent *types.Header) uint64 { func calcExcessBlobGas(isOsaka bool, bcfg BlobConfig, parent *types.Header) uint64 {
var parentExcessBlobGas, parentBlobGasUsed uint64 var parentExcessBlobGas, parentBlobGasUsed uint64
if parent.ExcessBlobGas != nil { if parent.ExcessBlobGas != nil {
parentExcessBlobGas = *parent.ExcessBlobGas parentExcessBlobGas = *parent.ExcessBlobGas
@ -169,8 +172,8 @@ func calcExcessBlobGas(isOsaka bool, bcfg *BlobConfig, parent *types.Header) uin
// CalcBlobFee calculates the blobfee from the header's excess blob gas field. // CalcBlobFee calculates the blobfee from the header's excess blob gas field.
func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int { func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
blobConfig := latestBlobConfig(config, header.Time) blobConfig, err := latestBlobConfig(config, header.Time)
if blobConfig == nil { if err != nil {
panic("calculating blob fee on unsupported fork") panic("calculating blob fee on unsupported fork")
} }
return blobConfig.blobBaseFee(*header.ExcessBlobGas) return blobConfig.blobBaseFee(*header.ExcessBlobGas)
@ -178,8 +181,8 @@ func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
// MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp. // MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp.
func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int { func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
blobConfig := latestBlobConfig(cfg, time) blobConfig, err := latestBlobConfig(cfg, time)
if blobConfig == nil { if err != nil {
return 0 return 0
} }
return blobConfig.Max return blobConfig.Max
@ -193,8 +196,8 @@ func MaxBlobGasPerBlock(cfg *params.ChainConfig, time uint64) uint64 {
// LatestMaxBlobsPerBlock returns the latest max blobs per block defined by the // LatestMaxBlobsPerBlock returns the latest max blobs per block defined by the
// configuration, regardless of the currently active fork. // configuration, regardless of the currently active fork.
func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int { func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
bcfg := latestBlobConfig(cfg, math.MaxUint64) bcfg, err := latestBlobConfig(cfg, math.MaxUint64)
if bcfg == nil { if err != nil {
return 0 return 0
} }
return bcfg.Max return bcfg.Max
@ -202,8 +205,8 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
// TargetBlobsPerBlock returns the target blobs per block for a block at the given timestamp. // TargetBlobsPerBlock returns the target blobs per block for a block at the given timestamp.
func TargetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int { func TargetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
blobConfig := latestBlobConfig(cfg, time) blobConfig, err := latestBlobConfig(cfg, time)
if blobConfig == nil { if err != nil {
return 0 return 0
} }
return blobConfig.Target return blobConfig.Target

View file

@ -80,12 +80,9 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
func NewEVMTxContext(msg *Message) vm.TxContext { func NewEVMTxContext(msg *Message) vm.TxContext {
ctx := vm.TxContext{ ctx := vm.TxContext{
Origin: msg.From, Origin: msg.From,
GasPrice: new(big.Int).Set(msg.GasPrice), GasPrice: uint256.MustFromBig(msg.GasPrice),
BlobHashes: msg.BlobHashes, BlobHashes: msg.BlobHashes,
} }
if msg.BlobGasFeeCap != nil {
ctx.BlobFeeCap = new(big.Int).Set(msg.BlobGasFeeCap)
}
return ctx return ctx
} }

View file

@ -39,7 +39,8 @@ type journalEntry interface {
revert(*StateDB) revert(*StateDB)
// dirtied returns the Ethereum address modified by this journal entry. // dirtied returns the Ethereum address modified by this journal entry.
dirtied() *common.Address // indicates false if no address was changed.
dirtied() (common.Address, bool)
// copy returns a deep-copied journal entry. // copy returns a deep-copied journal entry.
copy() journalEntry copy() journalEntry
@ -100,8 +101,8 @@ func (j *journal) revertToSnapshot(revid int, s *StateDB) {
// append inserts a new modification entry to the end of the change journal. // append inserts a new modification entry to the end of the change journal.
func (j *journal) append(entry journalEntry) { func (j *journal) append(entry journalEntry) {
j.entries = append(j.entries, entry) j.entries = append(j.entries, entry)
if addr := entry.dirtied(); addr != nil { if addr, dirty := entry.dirtied(); dirty {
j.dirties[*addr]++ j.dirties[addr]++
} }
} }
@ -113,9 +114,9 @@ func (j *journal) revert(statedb *StateDB, snapshot int) {
j.entries[i].revert(statedb) j.entries[i].revert(statedb)
// Drop any dirty tracking induced by the change // Drop any dirty tracking induced by the change
if addr := j.entries[i].dirtied(); addr != nil { if addr, dirty := j.entries[i].dirtied(); dirty {
if j.dirties[*addr]--; j.dirties[*addr] == 0 { if j.dirties[addr]--; j.dirties[addr] == 0 {
delete(j.dirties, *addr) delete(j.dirties, addr)
} }
} }
} }
@ -294,8 +295,8 @@ func (ch createObjectChange) revert(s *StateDB) {
delete(s.stateObjects, ch.account) delete(s.stateObjects, ch.account)
} }
func (ch createObjectChange) dirtied() *common.Address { func (ch createObjectChange) dirtied() (common.Address, bool) {
return &ch.account return ch.account, true
} }
func (ch createObjectChange) copy() journalEntry { func (ch createObjectChange) copy() journalEntry {
@ -308,8 +309,8 @@ func (ch createContractChange) revert(s *StateDB) {
s.getStateObject(ch.account).newContract = false s.getStateObject(ch.account).newContract = false
} }
func (ch createContractChange) dirtied() *common.Address { func (ch createContractChange) dirtied() (common.Address, bool) {
return nil return common.Address{}, false
} }
func (ch createContractChange) copy() journalEntry { func (ch createContractChange) copy() journalEntry {
@ -325,8 +326,8 @@ func (ch selfDestructChange) revert(s *StateDB) {
} }
} }
func (ch selfDestructChange) dirtied() *common.Address { func (ch selfDestructChange) dirtied() (common.Address, bool) {
return &ch.account return ch.account, true
} }
func (ch selfDestructChange) copy() journalEntry { func (ch selfDestructChange) copy() journalEntry {
@ -340,8 +341,8 @@ var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
func (ch touchChange) revert(s *StateDB) { func (ch touchChange) revert(s *StateDB) {
} }
func (ch touchChange) dirtied() *common.Address { func (ch touchChange) dirtied() (common.Address, bool) {
return &ch.account return ch.account, true
} }
func (ch touchChange) copy() journalEntry { func (ch touchChange) copy() journalEntry {
@ -354,8 +355,8 @@ func (ch balanceChange) revert(s *StateDB) {
s.getStateObject(ch.account).setBalance(ch.prev) s.getStateObject(ch.account).setBalance(ch.prev)
} }
func (ch balanceChange) dirtied() *common.Address { func (ch balanceChange) dirtied() (common.Address, bool) {
return &ch.account return ch.account, true
} }
func (ch balanceChange) copy() journalEntry { func (ch balanceChange) copy() journalEntry {
@ -369,8 +370,8 @@ func (ch nonceChange) revert(s *StateDB) {
s.getStateObject(ch.account).setNonce(ch.prev) s.getStateObject(ch.account).setNonce(ch.prev)
} }
func (ch nonceChange) dirtied() *common.Address { func (ch nonceChange) dirtied() (common.Address, bool) {
return &ch.account return ch.account, true
} }
func (ch nonceChange) copy() journalEntry { func (ch nonceChange) copy() journalEntry {
@ -384,8 +385,8 @@ func (ch codeChange) revert(s *StateDB) {
s.getStateObject(ch.account).setCode(crypto.Keccak256Hash(ch.prevCode), ch.prevCode) s.getStateObject(ch.account).setCode(crypto.Keccak256Hash(ch.prevCode), ch.prevCode)
} }
func (ch codeChange) dirtied() *common.Address { func (ch codeChange) dirtied() (common.Address, bool) {
return &ch.account return ch.account, true
} }
func (ch codeChange) copy() journalEntry { func (ch codeChange) copy() journalEntry {
@ -399,8 +400,8 @@ func (ch storageChange) revert(s *StateDB) {
s.getStateObject(ch.account).setState(ch.key, ch.prevvalue, ch.origvalue) s.getStateObject(ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
} }
func (ch storageChange) dirtied() *common.Address { func (ch storageChange) dirtied() (common.Address, bool) {
return &ch.account return ch.account, true
} }
func (ch storageChange) copy() journalEntry { func (ch storageChange) copy() journalEntry {
@ -416,8 +417,8 @@ func (ch transientStorageChange) revert(s *StateDB) {
s.setTransientState(ch.account, ch.key, ch.prevalue) s.setTransientState(ch.account, ch.key, ch.prevalue)
} }
func (ch transientStorageChange) dirtied() *common.Address { func (ch transientStorageChange) dirtied() (common.Address, bool) {
return nil return common.Address{}, false
} }
func (ch transientStorageChange) copy() journalEntry { func (ch transientStorageChange) copy() journalEntry {
@ -432,8 +433,8 @@ func (ch refundChange) revert(s *StateDB) {
s.refund = ch.prev s.refund = ch.prev
} }
func (ch refundChange) dirtied() *common.Address { func (ch refundChange) dirtied() (common.Address, bool) {
return nil return common.Address{}, false
} }
func (ch refundChange) copy() journalEntry { func (ch refundChange) copy() journalEntry {
@ -452,8 +453,8 @@ func (ch addLogChange) revert(s *StateDB) {
s.logSize-- s.logSize--
} }
func (ch addLogChange) dirtied() *common.Address { func (ch addLogChange) dirtied() (common.Address, bool) {
return nil return common.Address{}, false
} }
func (ch addLogChange) copy() journalEntry { func (ch addLogChange) copy() journalEntry {
@ -475,8 +476,8 @@ func (ch accessListAddAccountChange) revert(s *StateDB) {
s.accessList.DeleteAddress(ch.address) s.accessList.DeleteAddress(ch.address)
} }
func (ch accessListAddAccountChange) dirtied() *common.Address { func (ch accessListAddAccountChange) dirtied() (common.Address, bool) {
return nil return common.Address{}, false
} }
func (ch accessListAddAccountChange) copy() journalEntry { func (ch accessListAddAccountChange) copy() journalEntry {
@ -489,8 +490,8 @@ func (ch accessListAddSlotChange) revert(s *StateDB) {
s.accessList.DeleteSlot(ch.address, ch.slot) s.accessList.DeleteSlot(ch.address, ch.slot)
} }
func (ch accessListAddSlotChange) dirtied() *common.Address { func (ch accessListAddSlotChange) dirtied() (common.Address, bool) {
return nil return common.Address{}, false
} }
func (ch accessListAddSlotChange) copy() journalEntry { func (ch accessListAddSlotChange) copy() journalEntry {

View file

@ -50,7 +50,7 @@ func (s Storage) Copy() Storage {
type stateObject struct { type stateObject struct {
db *StateDB db *StateDB
address common.Address // address of ethereum account address common.Address // address of ethereum account
addrHash common.Hash // hash of ethereum address of the account addressHash *common.Hash // hash of ethereum address of the account
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
data types.StateAccount // Account data with all mutations applied in the scope of block data types.StateAccount // Account data with all mutations applied in the scope of block
@ -102,7 +102,6 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
return &stateObject{ return &stateObject{
db: db, db: db,
address: address, address: address,
addrHash: crypto.Keccak256Hash(address[:]),
origin: origin, origin: origin,
data: *acct, data: *acct,
originStorage: make(Storage), originStorage: make(Storage),
@ -112,6 +111,14 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
} }
} }
func (s *stateObject) addrHash() common.Hash {
if s.addressHash == nil {
h := crypto.Keccak256Hash(s.address[:])
s.addressHash = &h
}
return *s.addressHash
}
func (s *stateObject) markSelfdestructed() { func (s *stateObject) markSelfdestructed() {
s.selfDestructed = true s.selfDestructed = true
} }
@ -151,7 +158,7 @@ func (s *stateObject) getPrefetchedTrie() Trie {
return nil return nil
} }
// Attempt to retrieve the trie from the prefetcher // Attempt to retrieve the trie from the prefetcher
return s.db.prefetcher.trie(s.addrHash, s.data.Root) return s.db.prefetcher.trie(s.addrHash(), s.data.Root)
} }
// GetState retrieves a value associated with the given storage key. // GetState retrieves a value associated with the given storage key.
@ -203,7 +210,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
// Schedule the resolved storage slots for prefetching if it's enabled. // Schedule the resolved storage slots for prefetching if it's enabled.
if s.db.prefetcher != nil && s.data.Root != types.EmptyRootHash { if s.db.prefetcher != nil && s.data.Root != types.EmptyRootHash {
if err = s.db.prefetcher.prefetch(s.addrHash, s.origin.Root, s.address, nil, []common.Hash{key}, true); err != nil { if err = s.db.prefetcher.prefetch(s.addrHash(), s.origin.Root, s.address, nil, []common.Hash{key}, true); err != nil {
log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err) log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err)
} }
} }
@ -264,7 +271,7 @@ func (s *stateObject) finalise() {
s.pendingStorage[key] = value s.pendingStorage[key] = value
} }
if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash { if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
if err := s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, nil, slotsToPrefetch, false); err != nil { if err := s.db.prefetcher.prefetch(s.addrHash(), s.data.Root, s.address, nil, slotsToPrefetch, false); err != nil {
log.Error("Failed to prefetch slots", "addr", s.address, "slots", len(slotsToPrefetch), "err", err) log.Error("Failed to prefetch slots", "addr", s.address, "slots", len(slotsToPrefetch), "err", err)
} }
} }
@ -359,7 +366,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
s.db.StorageDeleted.Add(1) s.db.StorageDeleted.Add(1)
} }
if s.db.prefetcher != nil { if s.db.prefetcher != nil {
s.db.prefetcher.used(s.addrHash, s.data.Root, nil, used) s.db.prefetcher.used(s.addrHash(), s.data.Root, nil, used)
} }
s.uncommittedStorage = make(Storage) // empties the commit markers s.uncommittedStorage = make(Storage) // empties the commit markers
return tr, nil return tr, nil
@ -491,7 +498,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
obj := &stateObject{ obj := &stateObject{
db: db, db: db,
address: s.address, address: s.address,
addrHash: s.addrHash, addressHash: nil,
origin: s.origin, origin: s.origin,
data: s.data, data: s.data,
code: s.code, code: s.code,

View file

@ -867,13 +867,13 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
witness := trie.Witness() witness := trie.Witness()
s.witness.AddState(witness) s.witness.AddState(witness)
if s.witnessStats != nil { if s.witnessStats != nil {
s.witnessStats.Add(witness, obj.addrHash) s.witnessStats.Add(witness, obj.addrHash())
} }
} else if obj.trie != nil { } else if obj.trie != nil {
witness := obj.trie.Witness() witness := obj.trie.Witness()
s.witness.AddState(witness) s.witness.AddState(witness)
if s.witnessStats != nil { if s.witnessStats != nil {
s.witnessStats.Add(witness, obj.addrHash) s.witnessStats.Add(witness, obj.addrHash())
} }
} }
} }
@ -891,13 +891,13 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
witness := trie.Witness() witness := trie.Witness()
s.witness.AddState(witness) s.witness.AddState(witness)
if s.witnessStats != nil { if s.witnessStats != nil {
s.witnessStats.Add(witness, obj.addrHash) s.witnessStats.Add(witness, obj.addrHash())
} }
} else if obj.trie != nil { } else if obj.trie != nil {
witness := obj.trie.Witness() witness := obj.trie.Witness()
s.witness.AddState(witness) s.witness.AddState(witness)
if s.witnessStats != nil { if s.witnessStats != nil {
s.witnessStats.Add(witness, obj.addrHash) s.witnessStats.Add(witness, obj.addrHash())
} }
} }
} }
@ -1284,7 +1284,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum
return err return err
} }
lock.Lock() lock.Lock()
updates[obj.addrHash] = update updates[obj.addrHash()] = update
s.StorageCommits = time.Since(start) // overwrite with the longest storage commit runtime s.StorageCommits = time.Since(start) // overwrite with the longest storage commit runtime
lock.Unlock() lock.Unlock()
return nil return nil

View file

@ -330,7 +330,7 @@ func NewEIP2930Signer(chainId *big.Int) Signer {
// are replay-protected as well as unprotected homestead transactions. // are replay-protected as well as unprotected homestead transactions.
// Deprecated: always use the Signer interface type // Deprecated: always use the Signer interface type
type EIP155Signer struct { type EIP155Signer struct {
chainId, chainIdMul *big.Int chainId *big.Int
} }
func NewEIP155Signer(chainId *big.Int) EIP155Signer { func NewEIP155Signer(chainId *big.Int) EIP155Signer {
@ -339,7 +339,6 @@ func NewEIP155Signer(chainId *big.Int) EIP155Signer {
} }
return EIP155Signer{ return EIP155Signer{
chainId: chainId, chainId: chainId,
chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)),
} }
} }
@ -365,7 +364,8 @@ func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
} }
V, R, S := tx.RawSignatureValues() V, R, S := tx.RawSignatureValues()
V = new(big.Int).Sub(V, s.chainIdMul) V = new(big.Int).Sub(V, s.chainId)
V = new(big.Int).Sub(V, s.chainId)
V.Sub(V, big8) V.Sub(V, big8)
return recoverPlain(s.Hash(tx), R, S, V, true) return recoverPlain(s.Hash(tx), R, S, V, true)
} }
@ -382,7 +382,8 @@ func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big
} }
if s.chainId.Sign() != 0 { if s.chainId.Sign() != 0 {
V = big.NewInt(int64(sig[64] + 35)) V = big.NewInt(int64(sig[64] + 35))
V.Add(V, s.chainIdMul) V.Add(V, s.chainId)
V.Add(V, s.chainId)
} }
return R, S, V, nil return R, S, V, nil
} }

View file

@ -73,9 +73,8 @@ type BlockContext struct {
type TxContext struct { type TxContext struct {
// Message information // Message information
Origin common.Address // Provides information for ORIGIN Origin common.Address // Provides information for ORIGIN
GasPrice *big.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set) GasPrice *uint256.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set)
BlobHashes []common.Hash // Provides information for BLOBHASH BlobHashes []common.Hash // Provides information for BLOBHASH
BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set
AccessEvents *state.AccessEvents // Capture all state accesses for this tx AccessEvents *state.AccessEvents // Capture all state accesses for this tx
} }
@ -125,9 +124,6 @@ type EVM struct {
// jumpDests stores results of JUMPDEST analysis. // jumpDests stores results of JUMPDEST analysis.
jumpDests JumpDestCache jumpDests JumpDestCache
hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes
hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes
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
} }
@ -144,7 +140,6 @@ 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: newMapJumpDests(), jumpDests: newMapJumpDests(),
hasher: crypto.NewKeccakState(),
} }
evm.precompiles = activePrecompiledContracts(evm.chainRules) evm.precompiles = activePrecompiledContracts(evm.chainRules)
@ -618,7 +613,7 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:] // The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
inithash := crypto.HashData(evm.hasher, code) inithash := crypto.Keccak256Hash(code)
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:]) contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2) return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
} }

View file

@ -22,6 +22,7 @@ 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/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -233,14 +234,12 @@ func opKeccak256(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
offset, size := scope.Stack.pop(), scope.Stack.peek() offset, size := scope.Stack.pop(), scope.Stack.peek()
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
evm.hasher.Reset() hash := crypto.Keccak256Hash(data)
evm.hasher.Write(data)
evm.hasher.Read(evm.hasherBuf[:])
if evm.Config.EnablePreimageRecording { if evm.Config.EnablePreimageRecording {
evm.StateDB.AddPreimage(evm.hasherBuf, data) evm.StateDB.AddPreimage(hash, data)
} }
size.SetBytes(evm.hasherBuf[:]) size.SetBytes(hash[:])
return nil, nil return nil, nil
} }
@ -417,8 +416,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) {
v, _ := uint256.FromBig(evm.GasPrice) scope.Stack.push(evm.GasPrice.Clone())
scope.Stack.push(v)
return nil, nil return nil, nil
} }

View file

@ -19,14 +19,14 @@ package runtime
import ( import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/holiman/uint256"
) )
func NewEnv(cfg *Config) *vm.EVM { func NewEnv(cfg *Config) *vm.EVM {
txContext := vm.TxContext{ txContext := vm.TxContext{
Origin: cfg.Origin, Origin: cfg.Origin,
GasPrice: cfg.GasPrice, GasPrice: uint256.MustFromBig(cfg.GasPrice),
BlobHashes: cfg.BlobHashes, BlobHashes: cfg.BlobHashes,
BlobFeeCap: cfg.BlobFeeCap,
} }
blockContext := vm.BlockContext{ blockContext := vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,

View file

@ -46,7 +46,7 @@ type vmContext struct {
} }
func testCtx() *vmContext { func testCtx() *vmContext {
return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1), BaseFee: big.NewInt(0)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1), BaseFee: big.NewInt(0)}, txCtx: vm.TxContext{GasPrice: uint256.NewInt(100000)}}
} }
func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
@ -63,7 +63,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
contract.Code = contractCode contract.Code = contractCode
} }
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice}), contract.Caller()) tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice.ToBig()}), contract.Caller())
tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig())
ret, err := evm.Run(contract, []byte{}, false) ret, err := evm.Run(contract, []byte{}, false)
tracer.OnExit(0, ret, startGas-contract.Gas, err, true) tracer.OnExit(0, ret, startGas-contract.Gas, err, true)
@ -186,7 +186,7 @@ func TestHaltBetweenSteps(t *testing.T) {
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil),
} }
evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks})
evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(1)}) evm.SetTxContext(vm.TxContext{GasPrice: uint256.NewInt(1)})
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 0, big.NewInt(0)) tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 0, big.NewInt(0))
tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil) tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
@ -210,7 +210,7 @@ func TestNoStepExec(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks})
evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(100)}) evm.SetTxContext(vm.TxContext{GasPrice: uint256.NewInt(100)})
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0)) tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0))
tracer.OnExit(0, nil, 0, nil, false) tracer.OnExit(0, nil, 0, nil, false)
@ -240,7 +240,7 @@ func TestIsPrecompile(t *testing.T) {
chaincfg.ByzantiumBlock = big.NewInt(100) chaincfg.ByzantiumBlock = big.NewInt(100)
chaincfg.IstanbulBlock = big.NewInt(200) chaincfg.IstanbulBlock = big.NewInt(200)
chaincfg.BerlinBlock = big.NewInt(300) chaincfg.BerlinBlock = big.NewInt(300)
txCtx := vm.TxContext{GasPrice: big.NewInt(100000)} txCtx := vm.TxContext{GasPrice: uint256.NewInt(100000)}
tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil, chaincfg) tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil, chaincfg)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -1375,7 +1375,6 @@ func (err *ConfigCompatError) Error() string {
// Rules is a one time interface meaning that it shouldn't be used in between transition // Rules is a one time interface meaning that it shouldn't be used in between transition
// phases. // phases.
type Rules struct { type Rules struct {
ChainID *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsEIP2929, IsEIP4762 bool IsEIP2929, IsEIP4762 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
@ -1386,15 +1385,10 @@ type Rules struct {
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules { func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules {
chainID := c.ChainID
if chainID == nil {
chainID = new(big.Int)
}
// disallow setting Merge out of order // disallow setting Merge out of order
isMerge = isMerge && c.IsLondon(num) isMerge = isMerge && c.IsLondon(num)
isVerkle := isMerge && c.IsVerkle(num, timestamp) isVerkle := isMerge && c.IsVerkle(num, timestamp)
return Rules{ return Rules{
ChainID: new(big.Int).Set(chainID),
IsHomestead: c.IsHomestead(num), IsHomestead: c.IsHomestead(num),
IsEIP150: c.IsEIP150(num), IsEIP150: c.IsEIP150(num),
IsEIP155: c.IsEIP155(num), IsEIP155: c.IsEIP155(num),