From 1ef3bcab8f1657846f6ad6628f073dc90d4d4c23 Mon Sep 17 00:00:00 2001 From: jwasinger Date: Fri, 11 Jul 2025 20:51:04 +0900 Subject: [PATCH 01/56] core/types: add block-level access list structures with encoding/decoding (#31948) This adds the SSZ types from the [EIP-7928](https://eips.ethereum.org/EIPS/eip-7928) and also adds encoder/decoder generation using https://github.com/ferranbt/fastssz. The fastssz dependency is updated because the generation will not work properly with the master branch version due to a bug in fastssz. --------- Co-authored-by: Gary Rong --- core/types/bal/bal.go | 182 ++++++++++ core/types/bal/bal_encoding.go | 344 +++++++++++++++++++ core/types/bal/bal_encoding_rlp_generated.go | 280 +++++++++++++++ core/types/bal/bal_test.go | 252 ++++++++++++++ go.mod | 3 +- go.sum | 10 +- 6 files changed, 1066 insertions(+), 5 deletions(-) create mode 100644 core/types/bal/bal.go create mode 100644 core/types/bal/bal_encoding.go create mode 100644 core/types/bal/bal_encoding_rlp_generated.go create mode 100644 core/types/bal/bal_test.go diff --git a/core/types/bal/bal.go b/core/types/bal/bal.go new file mode 100644 index 0000000000..fca54f7681 --- /dev/null +++ b/core/types/bal/bal.go @@ -0,0 +1,182 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bal + +import ( + "bytes" + "maps" + + "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" +) + +// CodeChange contains the runtime bytecode deployed at an address and the +// transaction index where the deployment took place. +type CodeChange struct { + TxIndex uint16 + Code []byte `json:"code,omitempty"` +} + +// ConstructionAccountAccess contains post-block account state for mutations as well as +// all storage keys that were read during execution. It is used when building block +// access list during execution. +type ConstructionAccountAccess struct { + // StorageWrites is the post-state values of an account's storage slots + // that were modified in a block, keyed by the slot key and the tx index + // where the modification occurred. + StorageWrites map[common.Hash]map[uint16]common.Hash `json:"storageWrites,omitempty"` + + // StorageReads is the set of slot keys that were accessed during block + // execution. + // + // Storage slots which are both read and written (with changed values) + // appear only in StorageWrites. + StorageReads map[common.Hash]struct{} `json:"storageReads,omitempty"` + + // BalanceChanges contains the post-transaction balances of an account, + // keyed by transaction indices where it was changed. + BalanceChanges map[uint16]*uint256.Int `json:"balanceChanges,omitempty"` + + // NonceChanges contains the post-state nonce values of an account keyed + // by tx index. + NonceChanges map[uint16]uint64 `json:"nonceChanges,omitempty"` + + // CodeChange is only set for contract accounts which were deployed in + // the block. + CodeChange *CodeChange `json:"codeChange,omitempty"` +} + +// NewConstructionAccountAccess initializes the account access object. +func NewConstructionAccountAccess() *ConstructionAccountAccess { + return &ConstructionAccountAccess{ + StorageWrites: make(map[common.Hash]map[uint16]common.Hash), + StorageReads: make(map[common.Hash]struct{}), + BalanceChanges: make(map[uint16]*uint256.Int), + NonceChanges: make(map[uint16]uint64), + } +} + +// ConstructionBlockAccessList contains post-block modified state and some state accessed +// in execution (account addresses and storage keys). +type ConstructionBlockAccessList struct { + Accounts map[common.Address]*ConstructionAccountAccess +} + +// NewConstructionBlockAccessList instantiates an empty access list. +func NewConstructionBlockAccessList() ConstructionBlockAccessList { + return ConstructionBlockAccessList{ + Accounts: make(map[common.Address]*ConstructionAccountAccess), + } +} + +// AccountRead records the address of an account that has been read during execution. +func (b *ConstructionBlockAccessList) AccountRead(addr common.Address) { + if _, ok := b.Accounts[addr]; !ok { + b.Accounts[addr] = NewConstructionAccountAccess() + } +} + +// StorageRead records a storage key read during execution. +func (b *ConstructionBlockAccessList) StorageRead(address common.Address, key common.Hash) { + if _, ok := b.Accounts[address]; !ok { + b.Accounts[address] = NewConstructionAccountAccess() + } + if _, ok := b.Accounts[address].StorageWrites[key]; ok { + return + } + b.Accounts[address].StorageReads[key] = struct{}{} +} + +// StorageWrite records the post-transaction value of a mutated storage slot. +// The storage slot is removed from the list of read slots. +func (b *ConstructionBlockAccessList) StorageWrite(txIdx uint16, address common.Address, key, value common.Hash) { + if _, ok := b.Accounts[address]; !ok { + b.Accounts[address] = NewConstructionAccountAccess() + } + if _, ok := b.Accounts[address].StorageWrites[key]; !ok { + b.Accounts[address].StorageWrites[key] = make(map[uint16]common.Hash) + } + b.Accounts[address].StorageWrites[key][txIdx] = value + + delete(b.Accounts[address].StorageReads, key) +} + +// CodeChange records the code of a newly-created contract. +func (b *ConstructionBlockAccessList) CodeChange(address common.Address, txIndex uint16, code []byte) { + if _, ok := b.Accounts[address]; !ok { + b.Accounts[address] = NewConstructionAccountAccess() + } + b.Accounts[address].CodeChange = &CodeChange{ + TxIndex: txIndex, + Code: bytes.Clone(code), + } +} + +// NonceChange records tx post-state nonce of any contract-like accounts whose +// nonce was incremented. +func (b *ConstructionBlockAccessList) NonceChange(address common.Address, txIdx uint16, postNonce uint64) { + if _, ok := b.Accounts[address]; !ok { + b.Accounts[address] = NewConstructionAccountAccess() + } + b.Accounts[address].NonceChanges[txIdx] = postNonce +} + +// BalanceChange records the post-transaction balance of an account whose +// balance changed. +func (b *ConstructionBlockAccessList) BalanceChange(txIdx uint16, address common.Address, balance *uint256.Int) { + if _, ok := b.Accounts[address]; !ok { + b.Accounts[address] = NewConstructionAccountAccess() + } + b.Accounts[address].BalanceChanges[txIdx] = balance.Clone() +} + +// PrettyPrint returns a human-readable representation of the access list +func (b *ConstructionBlockAccessList) PrettyPrint() string { + enc := b.toEncodingObj() + return enc.PrettyPrint() +} + +// Copy returns a deep copy of the access list. +func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList { + res := NewConstructionBlockAccessList() + for addr, aa := range b.Accounts { + var aaCopy ConstructionAccountAccess + + slotWrites := make(map[common.Hash]map[uint16]common.Hash, len(aa.StorageWrites)) + for key, m := range aa.StorageWrites { + slotWrites[key] = maps.Clone(m) + } + aaCopy.StorageWrites = slotWrites + aaCopy.StorageReads = maps.Clone(aa.StorageReads) + + balances := make(map[uint16]*uint256.Int, len(aa.BalanceChanges)) + for index, balance := range aa.BalanceChanges { + balances[index] = balance.Clone() + } + aaCopy.BalanceChanges = balances + aaCopy.NonceChanges = maps.Clone(aa.NonceChanges) + + if aa.CodeChange != nil { + aaCopy.CodeChange = &CodeChange{ + TxIndex: aa.CodeChange.TxIndex, + Code: bytes.Clone(aa.CodeChange.Code), + } + } + res.Accounts[addr] = &aaCopy + } + return &res +} diff --git a/core/types/bal/bal_encoding.go b/core/types/bal/bal_encoding.go new file mode 100644 index 0000000000..d7d08801b1 --- /dev/null +++ b/core/types/bal/bal_encoding.go @@ -0,0 +1,344 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bal + +import ( + "bytes" + "cmp" + "errors" + "fmt" + "io" + "maps" + "slices" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" +) + +//go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -out bal_encoding_rlp_generated.go -type BlockAccessList -decoder + +// These are objects used as input for the access list encoding. They mirror +// the spec format. + +// BlockAccessList is the encoding format of ConstructionBlockAccessList. +type BlockAccessList struct { + Accesses []AccountAccess `ssz-max:"300000"` +} + +// Validate returns an error if the contents of the access list are not ordered +// according to the spec or any code changes are contained which exceed protocol +// max code size. +func (e *BlockAccessList) Validate() error { + if !slices.IsSortedFunc(e.Accesses, func(a, b AccountAccess) int { + return bytes.Compare(a.Address[:], b.Address[:]) + }) { + return errors.New("block access list accounts not in lexicographic order") + } + for _, entry := range e.Accesses { + if err := entry.validate(); err != nil { + return err + } + } + return nil +} + +// Hash computes the keccak256 hash of the access list +func (e *BlockAccessList) Hash() common.Hash { + var enc bytes.Buffer + err := e.EncodeRLP(&enc) + if err != nil { + // errors here are related to BAL values exceeding maximum size defined + // by the spec. Hard-fail because these cases are not expected to be hit + // under reasonable conditions. + panic(err) + } + return crypto.Keccak256Hash(enc.Bytes()) +} + +// encodeBalance encodes the provided balance into 16-bytes. +func encodeBalance(val *uint256.Int) [16]byte { + valBytes := val.Bytes() + if len(valBytes) > 16 { + panic("can't encode value that is greater than 16 bytes in size") + } + var enc [16]byte + copy(enc[16-len(valBytes):], valBytes[:]) + return enc +} + +// encodingBalanceChange is the encoding format of BalanceChange. +type encodingBalanceChange struct { + TxIdx uint16 `ssz-size:"2"` + Balance [16]byte `ssz-size:"16"` +} + +// encodingAccountNonce is the encoding format of NonceChange. +type encodingAccountNonce struct { + TxIdx uint16 `ssz-size:"2"` + Nonce uint64 `ssz-size:"8"` +} + +// encodingStorageWrite is the encoding format of StorageWrites. +type encodingStorageWrite struct { + TxIdx uint16 + ValueAfter [32]byte `ssz-size:"32"` +} + +// encodingStorageWrite is the encoding format of SlotWrites. +type encodingSlotWrites struct { + Slot [32]byte `ssz-size:"32"` + Accesses []encodingStorageWrite `ssz-max:"300000"` +} + +// validate returns an instance of the encoding-representation slot writes in +// working representation. +func (e *encodingSlotWrites) validate() error { + if slices.IsSortedFunc(e.Accesses, func(a, b encodingStorageWrite) int { + return cmp.Compare[uint16](a.TxIdx, b.TxIdx) + }) { + return nil + } + return errors.New("storage write tx indices not in order") +} + +// AccountAccess is the encoding format of ConstructionAccountAccess. +type AccountAccess struct { + Address [20]byte `ssz-size:"20"` // 20-byte Ethereum address + StorageWrites []encodingSlotWrites `ssz-max:"300000"` // Storage changes (slot -> [tx_index -> new_value]) + StorageReads [][32]byte `ssz-max:"300000"` // Read-only storage keys + BalanceChanges []encodingBalanceChange `ssz-max:"300000"` // Balance changes ([tx_index -> post_balance]) + NonceChanges []encodingAccountNonce `ssz-max:"300000"` // Nonce changes ([tx_index -> new_nonce]) + Code []CodeChange `ssz-max:"1"` // Code changes ([tx_index -> new_code]) +} + +// validate converts the account accesses out of encoding format. +// If any of the keys in the encoding object are not ordered according to the +// spec, an error is returned. +func (e *AccountAccess) validate() error { + // Check the storage write slots are sorted in order + if !slices.IsSortedFunc(e.StorageWrites, func(a, b encodingSlotWrites) int { + return bytes.Compare(a.Slot[:], b.Slot[:]) + }) { + return errors.New("storage writes slots not in lexicographic order") + } + for _, write := range e.StorageWrites { + if err := write.validate(); err != nil { + return err + } + } + + // Check the storage read slots are sorted in order + if !slices.IsSortedFunc(e.StorageReads, func(a, b [32]byte) int { + return bytes.Compare(a[:], b[:]) + }) { + return errors.New("storage read slots not in lexicographic order") + } + + // Check the balance changes are sorted in order + if !slices.IsSortedFunc(e.BalanceChanges, func(a, b encodingBalanceChange) int { + return cmp.Compare[uint16](a.TxIdx, b.TxIdx) + }) { + return errors.New("balance changes not in ascending order by tx index") + } + + // Check the nonce changes are sorted in order + if !slices.IsSortedFunc(e.NonceChanges, func(a, b encodingAccountNonce) int { + return cmp.Compare[uint16](a.TxIdx, b.TxIdx) + }) { + return errors.New("nonce changes not in ascending order by tx index") + } + + // Convert code change + if len(e.Code) == 1 { + if len(e.Code[0].Code) > params.MaxCodeSize { + return fmt.Errorf("code change contained oversized code") + } + } + return nil +} + +// Copy returns a deep copy of the account access +func (e *AccountAccess) Copy() AccountAccess { + res := AccountAccess{ + Address: e.Address, + StorageReads: slices.Clone(e.StorageReads), + BalanceChanges: slices.Clone(e.BalanceChanges), + NonceChanges: slices.Clone(e.NonceChanges), + } + for _, storageWrite := range e.StorageWrites { + res.StorageWrites = append(res.StorageWrites, encodingSlotWrites{ + Slot: storageWrite.Slot, + Accesses: slices.Clone(storageWrite.Accesses), + }) + } + if len(e.Code) == 1 { + res.Code = []CodeChange{ + { + e.Code[0].TxIndex, + bytes.Clone(e.Code[0].Code), + }, + } + } + return res +} + +// EncodeRLP returns the RLP-encoded access list +func (b *ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error { + return b.toEncodingObj().EncodeRLP(wr) +} + +var _ rlp.Encoder = &ConstructionBlockAccessList{} + +// toEncodingObj creates an instance of the ConstructionAccountAccess of the type that is +// used as input for the encoding. +func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAccess { + res := AccountAccess{ + Address: addr, + StorageWrites: make([]encodingSlotWrites, 0), + StorageReads: make([][32]byte, 0), + BalanceChanges: make([]encodingBalanceChange, 0), + NonceChanges: make([]encodingAccountNonce, 0), + Code: nil, + } + + // Convert write slots + writeSlots := slices.Collect(maps.Keys(a.StorageWrites)) + slices.SortFunc(writeSlots, common.Hash.Cmp) + for _, slot := range writeSlots { + var obj encodingSlotWrites + obj.Slot = slot + + slotWrites := a.StorageWrites[slot] + obj.Accesses = make([]encodingStorageWrite, 0, len(slotWrites)) + + indices := slices.Collect(maps.Keys(slotWrites)) + slices.SortFunc(indices, cmp.Compare[uint16]) + for _, index := range indices { + obj.Accesses = append(obj.Accesses, encodingStorageWrite{ + TxIdx: index, + ValueAfter: slotWrites[index], + }) + } + res.StorageWrites = append(res.StorageWrites, obj) + } + + // Convert read slots + readSlots := slices.Collect(maps.Keys(a.StorageReads)) + slices.SortFunc(readSlots, common.Hash.Cmp) + for _, slot := range readSlots { + res.StorageReads = append(res.StorageReads, slot) + } + + // Convert balance changes + balanceIndices := slices.Collect(maps.Keys(a.BalanceChanges)) + slices.SortFunc(balanceIndices, cmp.Compare[uint16]) + for _, idx := range balanceIndices { + res.BalanceChanges = append(res.BalanceChanges, encodingBalanceChange{ + TxIdx: idx, + Balance: encodeBalance(a.BalanceChanges[idx]), + }) + } + + // Convert nonce changes + nonceIndices := slices.Collect(maps.Keys(a.NonceChanges)) + slices.SortFunc(nonceIndices, cmp.Compare[uint16]) + for _, idx := range nonceIndices { + res.NonceChanges = append(res.NonceChanges, encodingAccountNonce{ + TxIdx: idx, + Nonce: a.NonceChanges[idx], + }) + } + + // Convert code change + if a.CodeChange != nil { + res.Code = []CodeChange{ + { + a.CodeChange.TxIndex, + bytes.Clone(a.CodeChange.Code), + }, + } + } + return res +} + +// toEncodingObj returns an instance of the access list expressed as the type +// which is used as input for the encoding/decoding. +func (b *ConstructionBlockAccessList) toEncodingObj() *BlockAccessList { + var addresses []common.Address + for addr := range b.Accounts { + addresses = append(addresses, addr) + } + slices.SortFunc(addresses, common.Address.Cmp) + + var res BlockAccessList + for _, addr := range addresses { + res.Accesses = append(res.Accesses, b.Accounts[addr].toEncodingObj(addr)) + } + return &res +} + +func (e *BlockAccessList) PrettyPrint() string { + var res bytes.Buffer + printWithIndent := func(indent int, text string) { + fmt.Fprintf(&res, "%s%s\n", strings.Repeat(" ", indent), text) + } + for _, accountDiff := range e.Accesses { + printWithIndent(0, fmt.Sprintf("%x:", accountDiff.Address)) + + printWithIndent(1, "storage writes:") + for _, sWrite := range accountDiff.StorageWrites { + printWithIndent(2, fmt.Sprintf("%x:", sWrite.Slot)) + for _, access := range sWrite.Accesses { + printWithIndent(3, fmt.Sprintf("%d: %x", access.TxIdx, access.ValueAfter)) + } + } + + printWithIndent(1, "storage reads:") + for _, slot := range accountDiff.StorageReads { + printWithIndent(2, fmt.Sprintf("%x", slot)) + } + + printWithIndent(1, "balance changes:") + for _, change := range accountDiff.BalanceChanges { + balance := new(uint256.Int).SetBytes(change.Balance[:]).String() + printWithIndent(2, fmt.Sprintf("%d: %s", change.TxIdx, balance)) + } + + printWithIndent(1, "nonce changes:") + for _, change := range accountDiff.NonceChanges { + printWithIndent(2, fmt.Sprintf("%d: %d", change.TxIdx, change.Nonce)) + } + + if len(accountDiff.Code) > 0 { + printWithIndent(1, "code:") + printWithIndent(2, fmt.Sprintf("%d: %x", accountDiff.Code[0].TxIndex, accountDiff.Code[0].Code)) + } + } + return res.String() +} + +// Copy returns a deep copy of the access list +func (e *BlockAccessList) Copy() (res BlockAccessList) { + for _, accountAccess := range e.Accesses { + res.Accesses = append(res.Accesses, accountAccess.Copy()) + } + return +} diff --git a/core/types/bal/bal_encoding_rlp_generated.go b/core/types/bal/bal_encoding_rlp_generated.go new file mode 100644 index 0000000000..0d52395329 --- /dev/null +++ b/core/types/bal/bal_encoding_rlp_generated.go @@ -0,0 +1,280 @@ +// Code generated by rlpgen. DO NOT EDIT. + +package bal + +import "github.com/ethereum/go-ethereum/rlp" +import "io" + +func (obj *BlockAccessList) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + _tmp1 := w.List() + for _, _tmp2 := range obj.Accesses { + _tmp3 := w.List() + w.WriteBytes(_tmp2.Address[:]) + _tmp4 := w.List() + for _, _tmp5 := range _tmp2.StorageWrites { + _tmp6 := w.List() + w.WriteBytes(_tmp5.Slot[:]) + _tmp7 := w.List() + for _, _tmp8 := range _tmp5.Accesses { + _tmp9 := w.List() + w.WriteUint64(uint64(_tmp8.TxIdx)) + w.WriteBytes(_tmp8.ValueAfter[:]) + w.ListEnd(_tmp9) + } + w.ListEnd(_tmp7) + w.ListEnd(_tmp6) + } + w.ListEnd(_tmp4) + _tmp10 := w.List() + for _, _tmp11 := range _tmp2.StorageReads { + w.WriteBytes(_tmp11[:]) + } + w.ListEnd(_tmp10) + _tmp12 := w.List() + for _, _tmp13 := range _tmp2.BalanceChanges { + _tmp14 := w.List() + w.WriteUint64(uint64(_tmp13.TxIdx)) + w.WriteBytes(_tmp13.Balance[:]) + w.ListEnd(_tmp14) + } + w.ListEnd(_tmp12) + _tmp15 := w.List() + for _, _tmp16 := range _tmp2.NonceChanges { + _tmp17 := w.List() + w.WriteUint64(uint64(_tmp16.TxIdx)) + w.WriteUint64(_tmp16.Nonce) + w.ListEnd(_tmp17) + } + w.ListEnd(_tmp15) + _tmp18 := w.List() + for _, _tmp19 := range _tmp2.Code { + _tmp20 := w.List() + w.WriteUint64(uint64(_tmp19.TxIndex)) + w.WriteBytes(_tmp19.Code) + w.ListEnd(_tmp20) + } + w.ListEnd(_tmp18) + w.ListEnd(_tmp3) + } + w.ListEnd(_tmp1) + w.ListEnd(_tmp0) + return w.Flush() +} + +func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error { + var _tmp0 BlockAccessList + { + if _, err := dec.List(); err != nil { + return err + } + // Accesses: + var _tmp1 []AccountAccess + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp2 AccountAccess + { + if _, err := dec.List(); err != nil { + return err + } + // Address: + var _tmp3 [20]byte + if err := dec.ReadBytes(_tmp3[:]); err != nil { + return err + } + _tmp2.Address = _tmp3 + // StorageWrites: + var _tmp4 []encodingSlotWrites + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp5 encodingSlotWrites + { + if _, err := dec.List(); err != nil { + return err + } + // Slot: + var _tmp6 [32]byte + if err := dec.ReadBytes(_tmp6[:]); err != nil { + return err + } + _tmp5.Slot = _tmp6 + // Accesses: + var _tmp7 []encodingStorageWrite + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp8 encodingStorageWrite + { + if _, err := dec.List(); err != nil { + return err + } + // TxIdx: + _tmp9, err := dec.Uint16() + if err != nil { + return err + } + _tmp8.TxIdx = _tmp9 + // ValueAfter: + var _tmp10 [32]byte + if err := dec.ReadBytes(_tmp10[:]); err != nil { + return err + } + _tmp8.ValueAfter = _tmp10 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp7 = append(_tmp7, _tmp8) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp5.Accesses = _tmp7 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp4 = append(_tmp4, _tmp5) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp2.StorageWrites = _tmp4 + // StorageReads: + var _tmp11 [][32]byte + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp12 [32]byte + if err := dec.ReadBytes(_tmp12[:]); err != nil { + return err + } + _tmp11 = append(_tmp11, _tmp12) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp2.StorageReads = _tmp11 + // BalanceChanges: + var _tmp13 []encodingBalanceChange + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp14 encodingBalanceChange + { + if _, err := dec.List(); err != nil { + return err + } + // TxIdx: + _tmp15, err := dec.Uint16() + if err != nil { + return err + } + _tmp14.TxIdx = _tmp15 + // Balance: + var _tmp16 [16]byte + if err := dec.ReadBytes(_tmp16[:]); err != nil { + return err + } + _tmp14.Balance = _tmp16 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp13 = append(_tmp13, _tmp14) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp2.BalanceChanges = _tmp13 + // NonceChanges: + var _tmp17 []encodingAccountNonce + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp18 encodingAccountNonce + { + if _, err := dec.List(); err != nil { + return err + } + // TxIdx: + _tmp19, err := dec.Uint16() + if err != nil { + return err + } + _tmp18.TxIdx = _tmp19 + // Nonce: + _tmp20, err := dec.Uint64() + if err != nil { + return err + } + _tmp18.Nonce = _tmp20 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp17 = append(_tmp17, _tmp18) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp2.NonceChanges = _tmp17 + // Code: + var _tmp21 []CodeChange + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp22 CodeChange + { + if _, err := dec.List(); err != nil { + return err + } + // TxIndex: + _tmp23, err := dec.Uint16() + if err != nil { + return err + } + _tmp22.TxIndex = _tmp23 + // Code: + _tmp24, err := dec.Bytes() + if err != nil { + return err + } + _tmp22.Code = _tmp24 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp21 = append(_tmp21, _tmp22) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp2.Code = _tmp21 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp1 = append(_tmp1, _tmp2) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp0.Accesses = _tmp1 + if err := dec.ListEnd(); err != nil { + return err + } + } + *obj = _tmp0 + return nil +} diff --git a/core/types/bal/bal_test.go b/core/types/bal/bal_test.go new file mode 100644 index 0000000000..29414e414e --- /dev/null +++ b/core/types/bal/bal_test.go @@ -0,0 +1,252 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bal + +import ( + "bytes" + "cmp" + "reflect" + "slices" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/internal/testrand" + "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" +) + +func equalBALs(a *BlockAccessList, b *BlockAccessList) bool { + if !reflect.DeepEqual(a, b) { + return false + } + return true +} + +func makeTestConstructionBAL() *ConstructionBlockAccessList { + return &ConstructionBlockAccessList{ + map[common.Address]*ConstructionAccountAccess{ + common.BytesToAddress([]byte{0xff, 0xff}): { + StorageWrites: map[common.Hash]map[uint16]common.Hash{ + common.BytesToHash([]byte{0x01}): { + 1: common.BytesToHash([]byte{1, 2, 3, 4}), + 2: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6}), + }, + common.BytesToHash([]byte{0x10}): { + 20: common.BytesToHash([]byte{1, 2, 3, 4}), + }, + }, + StorageReads: map[common.Hash]struct{}{ + common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7}): {}, + }, + BalanceChanges: map[uint16]*uint256.Int{ + 1: uint256.NewInt(100), + 2: uint256.NewInt(500), + }, + NonceChanges: map[uint16]uint64{ + 1: 2, + 2: 6, + }, + CodeChange: &CodeChange{ + TxIndex: 0, + Code: common.Hex2Bytes("deadbeef"), + }, + }, + common.BytesToAddress([]byte{0xff, 0xff, 0xff}): { + StorageWrites: map[common.Hash]map[uint16]common.Hash{ + common.BytesToHash([]byte{0x01}): { + 2: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6}), + 3: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}), + }, + common.BytesToHash([]byte{0x10}): { + 21: common.BytesToHash([]byte{1, 2, 3, 4, 5}), + }, + }, + StorageReads: map[common.Hash]struct{}{ + common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}): {}, + }, + BalanceChanges: map[uint16]*uint256.Int{ + 2: uint256.NewInt(100), + 3: uint256.NewInt(500), + }, + NonceChanges: map[uint16]uint64{ + 1: 2, + }, + }, + }, + } +} + +// TestBALEncoding tests that a populated access list can be encoded/decoded correctly. +func TestBALEncoding(t *testing.T) { + var buf bytes.Buffer + bal := makeTestConstructionBAL() + err := bal.EncodeRLP(&buf) + if err != nil { + t.Fatalf("encoding failed: %v\n", err) + } + var dec BlockAccessList + if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 10000000)); err != nil { + t.Fatalf("decoding failed: %v\n", err) + } + if dec.Hash() != bal.toEncodingObj().Hash() { + t.Fatalf("encoded block hash doesn't match decoded") + } + if !equalBALs(bal.toEncodingObj(), &dec) { + t.Fatal("decoded BAL doesn't match") + } +} + +func makeTestAccountAccess(sort bool) AccountAccess { + var ( + storageWrites []encodingSlotWrites + storageReads [][32]byte + balances []encodingBalanceChange + nonces []encodingAccountNonce + ) + for i := 0; i < 5; i++ { + slot := encodingSlotWrites{ + Slot: testrand.Hash(), + } + for j := 0; j < 3; j++ { + slot.Accesses = append(slot.Accesses, encodingStorageWrite{ + TxIdx: uint16(2 * j), + ValueAfter: testrand.Hash(), + }) + } + if sort { + slices.SortFunc(slot.Accesses, func(a, b encodingStorageWrite) int { + return cmp.Compare[uint16](a.TxIdx, b.TxIdx) + }) + } + storageWrites = append(storageWrites, slot) + } + if sort { + slices.SortFunc(storageWrites, func(a, b encodingSlotWrites) int { + return bytes.Compare(a.Slot[:], b.Slot[:]) + }) + } + + for i := 0; i < 5; i++ { + storageReads = append(storageReads, testrand.Hash()) + } + if sort { + slices.SortFunc(storageReads, func(a, b [32]byte) int { + return bytes.Compare(a[:], b[:]) + }) + } + + for i := 0; i < 5; i++ { + balances = append(balances, encodingBalanceChange{ + TxIdx: uint16(2 * i), + Balance: [16]byte(testrand.Bytes(16)), + }) + } + if sort { + slices.SortFunc(balances, func(a, b encodingBalanceChange) int { + return cmp.Compare[uint16](a.TxIdx, b.TxIdx) + }) + } + + for i := 0; i < 5; i++ { + nonces = append(nonces, encodingAccountNonce{ + TxIdx: uint16(2 * i), + Nonce: uint64(i + 100), + }) + } + if sort { + slices.SortFunc(nonces, func(a, b encodingAccountNonce) int { + return cmp.Compare[uint16](a.TxIdx, b.TxIdx) + }) + } + + return AccountAccess{ + Address: [20]byte(testrand.Bytes(20)), + StorageWrites: storageWrites, + StorageReads: storageReads, + BalanceChanges: balances, + NonceChanges: nonces, + Code: []CodeChange{ + { + TxIndex: 100, + Code: testrand.Bytes(256), + }, + }, + } +} + +func makeTestBAL(sort bool) BlockAccessList { + list := BlockAccessList{} + for i := 0; i < 5; i++ { + list.Accesses = append(list.Accesses, makeTestAccountAccess(sort)) + } + if sort { + slices.SortFunc(list.Accesses, func(a, b AccountAccess) int { + return bytes.Compare(a.Address[:], b.Address[:]) + }) + } + return list +} + +func TestBlockAccessListCopy(t *testing.T) { + list := makeTestBAL(true) + cpy := list.Copy() + cpyCpy := cpy.Copy() + + if !reflect.DeepEqual(list, cpy) { + t.Fatal("block access mismatch") + } + if !reflect.DeepEqual(cpy, cpyCpy) { + t.Fatal("block access mismatch") + } + + // Make sure the mutations on copy won't affect the origin + for _, aa := range cpyCpy.Accesses { + for i := 0; i < len(aa.StorageReads); i++ { + aa.StorageReads[i] = [32]byte(testrand.Bytes(32)) + } + } + if !reflect.DeepEqual(list, cpy) { + t.Fatal("block access mismatch") + } +} + +func TestBlockAccessListValidation(t *testing.T) { + // Validate the block access list after RLP decoding + enc := makeTestBAL(true) + if err := enc.Validate(); err != nil { + t.Fatalf("Unexpected validation error: %v", err) + } + var buf bytes.Buffer + if err := enc.EncodeRLP(&buf); err != nil { + t.Fatalf("Unexpected encoding error: %v", err) + } + + var dec BlockAccessList + if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil { + t.Fatalf("Unexpected RLP-decode error: %v", err) + } + if err := dec.Validate(); err != nil { + t.Fatalf("Unexpected validation error: %v", err) + } + + // Validate the derived block access list + cBAL := makeTestConstructionBAL() + listB := cBAL.toEncodingObj() + if err := listB.Validate(); err != nil { + t.Fatalf("Unexpected validation error: %v", err) + } +} diff --git a/go.mod b/go.mod index c3b27d405e..6b63450a91 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/ethereum/c-kzg-4844/v2 v2.1.0 github.com/ethereum/go-verkle v0.2.2 github.com/fatih/color v1.16.0 - github.com/ferranbt/fastssz v0.1.2 + github.com/ferranbt/fastssz v0.1.4 github.com/fjl/gencodec v0.1.0 github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff @@ -101,6 +101,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect + github.com/emicklei/dot v1.6.2 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect diff --git a/go.sum b/go.sum index 6f31f96ec2..db59c74229 100644 --- a/go.sum +++ b/go.sum @@ -108,14 +108,16 @@ github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 h1:+3HCtB74++ClLy8GgjU github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= -github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= +github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= github.com/fjl/gencodec v0.1.0 h1:B3K0xPfc52cw52BBgUbSPxYo+HlLfAgWMVKRWXUXBcs= github.com/fjl/gencodec v0.1.0/go.mod h1:Um1dFHPONZGTHog1qD1NaWjXJW/SPB38wPv0O8uZ2fI= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -316,8 +318,8 @@ github.com/protolambda/zrnt v0.34.1 h1:qW55rnhZJDnOb3TwFiFRJZi3yTXFrJdGOFQM7vCwY github.com/protolambda/zrnt v0.34.1/go.mod h1:A0fezkp9Tt3GBLATSPIbuY4ywYESyAuc/FFmPKg8Lqs= github.com/protolambda/ztyp v0.2.2 h1:rVcL3vBu9W/aV646zF6caLS/dyn9BN8NYiuJzicLNyY= github.com/protolambda/ztyp v0.2.2/go.mod h1:9bYgKGqg3wJqT9ac1gI2hnVb0STQq7p/1lapqrqY1dU= -github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48 h1:cSo6/vk8YpvkLbk9v3FO97cakNmUoxwi2KMP8hd5WIw= -github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48/go.mod h1:4pWaT30XoEx1j8KNJf3TV+E3mQkaufn7mf+jRNb/Fuk= +github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= +github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= From 071372553e0c76a23bca0075bc45aa49a6957b48 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 11 Jul 2025 19:56:16 +0800 Subject: [PATCH 02/56] eth/downloader: fix ancient limit in snap sync (#32188) This pull request fixes an issue in disabling direct-ancient mode in snap sync. Specifically, if `origin >= frozen && origin != 0`, it implies a part of chain data has been written into the key-value store, all the following writes into ancient store scheduled by downloader will be rejected with error `ERROR[07-10|03:46:57.924] Error importing chain data to ancients err="can't add block 1166 hash: the append operation is out-order: have 1166 want 0"`. This issue is detected by the https://github.com/ethpandaops/kurtosis-sync-test, which initiates the first snap sync cycle without the finalized header and implicitly disables the direct-ancient mode. A few seconds later the second snap sync cycle is initiated with the finalized information and direct-ancient mode is enabled incorrectly. --- eth/downloader/downloader.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 539aaef40e..dcda4e521c 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -535,9 +535,15 @@ func (d *Downloader) syncToHead() (err error) { // If a part of blockchain data has already been written into active store, // disable the ancient style insertion explicitly. - if origin >= frozen && frozen != 0 { + if origin >= frozen && origin != 0 { d.ancientLimit = 0 - log.Info("Disabling direct-ancient mode", "origin", origin, "ancient", frozen-1) + var ancient string + if frozen == 0 { + ancient = "null" + } else { + ancient = fmt.Sprintf("%d", frozen-1) + } + log.Info("Disabling direct-ancient mode", "origin", origin, "ancient", ancient) } else if d.ancientLimit > 0 { log.Debug("Enabling direct-ancient mode", "ancient", d.ancientLimit) } From 8cf87c65e973ae78a9509aa8186a2789c81c8399 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 11 Jul 2025 14:58:21 +0200 Subject: [PATCH 03/56] .github: remove karalabe from CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 681b5e7c66..900ff52e52 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -29,5 +29,5 @@ miner/ @MariusVanDerWijden @fjl @rjl493456442 node/ @fjl p2p/ @fjl @zsfelfoldi rlp/ @fjl -params/ @fjl @karalabe @gballet @rjl493456442 @zsfelfoldi +params/ @fjl @gballet @rjl493456442 @zsfelfoldi rpc/ @fjl From b992b105ef351033ec88fa00fb82dfab97a819dd Mon Sep 17 00:00:00 2001 From: PixelPilot <161360836+PixelPil0t1@users.noreply.github.com> Date: Fri, 11 Jul 2025 21:55:18 +0200 Subject: [PATCH 04/56] cmd/geth: update vcheck testdata, add docs on generating signatures (#32121) Fixed typo in security release URL by replacing: Old: https://blog.ethereum.org/2020/11/12/geth_security_release/ New: https://blog.ethereum.org/2020/11/12/geth-security-release/ --------- Co-authored-by: lightclient --- cmd/geth/testdata/vcheck/data.json | 161 ++++++++++++++++-- .../vcheck/minisig-sigs-new/data.json.minisig | 6 +- .../vulnerabilities.json.minisig.1 | 6 +- .../vulnerabilities.json.minisig.2 | 4 +- .../vulnerabilities.json.minisig.3 | 6 +- .../vcheck/signify-sigs/data.json.sig | 4 +- .../sigs/vulnerabilities.json.minisig.1 | 4 - .../sigs/vulnerabilities.json.minisig.2 | 4 - .../sigs/vulnerabilities.json.minisig.3 | 4 - cmd/geth/testdata/vcheck/vulnerabilities.json | 8 +- cmd/geth/version_check_test.go | 6 +- 11 files changed, 173 insertions(+), 40 deletions(-) delete mode 100644 cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.1 delete mode 100644 cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.2 delete mode 100644 cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.3 diff --git a/cmd/geth/testdata/vcheck/data.json b/cmd/geth/testdata/vcheck/data.json index e7ee2bf7e4..e52fd84e67 100644 --- a/cmd/geth/testdata/vcheck/data.json +++ b/cmd/geth/testdata/vcheck/data.json @@ -6,28 +6,33 @@ "description": "A mining flaw could cause miners to erroneously calculate PoW, due to an index overflow, if DAG size is exceeding the maximum 32 bit unsigned value.\n\nThis occurred on the ETC chain on 2020-11-06. This is likely to trigger for ETH mainnet around block `11550000`/epoch `385`, slated to occur early January 2021.\n\nThis issue is relevant only for miners, non-mining nodes are unaffected, since non-mining nodes use a smaller verification cache instead of a full DAG.", "links": [ "https://github.com/ethereum/go-ethereum/pull/21793", - "https://blog.ethereum.org/2020/11/12/geth_security_release/", - "https://github.com/ethereum/go-ethereum/commit/567d41d9363706b4b13ce0903804e8acf214af49" + "https://blog.ethereum.org/2020/11/12/geth-security-release", + "https://github.com/ethereum/go-ethereum/commit/567d41d9363706b4b13ce0903804e8acf214af49", + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-v592-xf75-856p" ], "introduced": "v1.6.0", "fixed": "v1.9.24", "published": "2020-11-12", "severity": "Medium", - "check": "Geth\\/v1\\.(6|7|8)\\..*|Geth\\/v1\\.9\\.2(1|2|3)-.*" + "CVE": "CVE-2020-26240", + "check": "Geth\\/v1\\.(6|7|8)\\..*|Geth\\/v1\\.9\\.\\d-.*|Geth\\/v1\\.9\\.1.*|Geth\\/v1\\.9\\.2(0|1|2|3)-.*" }, { - "name": "GoCrash", + "name": "Denial of service due to Go CVE-2020-28362", "uid": "GETH-2020-02", "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing, due to an underlying bug in Go (CVE-2020-28362) versions < `1.15.5`, or `<1.14.12`", "description": "The DoS issue can be used to crash all Geth nodes during block processing, the effects of which would be that a major part of the Ethereum network went offline.\n\nOutside of Go-Ethereum, the issue is most likely relevant for all forks of Geth (such as TurboGeth or ETC’s core-geth) which is built with versions of Go which contains the vulnerability.", "links": [ - "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://blog.ethereum.org/2020/11/12/geth-security-release", "https://groups.google.com/g/golang-announce/c/NpBGTTmKzpM", - "https://github.com/golang/go/issues/42552" + "https://github.com/golang/go/issues/42552", + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-m6gx-rhvj-fh52" ], + "introduced": "v0.0.0", "fixed": "v1.9.24", "published": "2020-11-12", "severity": "Critical", + "CVE": "CVE-2020-28362", "check": "Geth.*\\/go1\\.(11(.*)|12(.*)|13(.*)|14|14\\.(\\d|10|11|)|15|15\\.[0-4])$" }, { @@ -36,26 +41,162 @@ "summary": "A consensus flaw in Geth, related to `datacopy` precompile", "description": "Geth erroneously performed a 'shallow' copy when the precompiled `datacopy` (at `0x00...04`) was invoked. An attacker could deploy a contract that uses the shallow copy to corrupt the contents of the `RETURNDATA`, thus causing a consensus failure.", "links": [ - "https://blog.ethereum.org/2020/11/12/geth_security_release/" + "https://blog.ethereum.org/2020/11/12/geth-security-release", + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-69v6-xc2j-r2jf" ], "introduced": "v1.9.7", "fixed": "v1.9.17", "published": "2020-11-12", "severity": "Critical", + "CVE": "CVE-2020-26241", "check": "Geth\\/v1\\.9\\.(7|8|9|10|11|12|13|14|15|16).*$" }, { - "name": "GethCrash", + "name": "Geth DoS via MULMOD", "uid": "GETH-2020-04", "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing", - "description": "Full details to be disclosed at a later date", + "description": "Affected versions suffer from a vulnerability which can be exploited through the `MULMOD` operation, by specifying a modulo of `0`: `mulmod(a,b,0)`, causing a `panic` in the underlying library. \nThe crash was in the `uint256` library, where a buffer [underflowed](https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L442).\n\n\tif `d == 0`, `dLen` remains `0`\n\nand https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L451 will try to access index `[-1]`.\n\nThe `uint256` library was first merged in this [commit](https://github.com/ethereum/go-ethereum/commit/cf6674539c589f80031f3371a71c6a80addbe454), on 2020-06-08. \nExploiting this vulnerabilty would cause all vulnerable nodes to drop off the network. \n\nThe issue was brought to our attention through a [bug report](https://github.com/ethereum/go-ethereum/issues/21367), showing a `panic` occurring on sync from genesis on the Ropsten network.\n \nIt was estimated that the least obvious way to fix this would be to merge the fix into `uint256`, make a new release of that library and then update the geth-dependency.\n", "links": [ - "https://blog.ethereum.org/2020/11/12/geth_security_release/" + "https://blog.ethereum.org/2020/11/12/geth-security-release", + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-jm5c-rv3w-w83m", + "https://github.com/holiman/uint256/releases/tag/v1.1.1", + "https://github.com/holiman/uint256/pull/80", + "https://github.com/ethereum/go-ethereum/pull/21368" ], "introduced": "v1.9.16", "fixed": "v1.9.18", "published": "2020-11-12", "severity": "Critical", + "CVE": "CVE-2020-26242", "check": "Geth\\/v1\\.9.(16|17).*$" + }, + { + "name": "LES Server DoS via GetProofsV2", + "uid": "GETH-2020-05", + "summary": "A DoS vulnerability can make a LES server crash.", + "description": "A DoS vulnerability can make a LES server crash via malicious GetProofsV2 request from a connected LES client.\n\nThe vulnerability was patched in #21896.\n\nThis vulnerability only concern users explicitly running geth as a light server", + "links": [ + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-r33q-22hv-j29q", + "https://github.com/ethereum/go-ethereum/pull/21896" + ], + "introduced": "v1.8.0", + "fixed": "v1.9.25", + "published": "2020-12-10", + "severity": "Medium", + "CVE": "CVE-2020-26264", + "check": "(Geth\\/v1\\.8\\.*)|(Geth\\/v1\\.9\\.\\d-.*)|(Geth\\/v1\\.9\\.1\\d-.*)|(Geth\\/v1\\.9\\.(20|21|22|23|24)-.*)$" + }, + { + "name": "SELFDESTRUCT-recreate consensus flaw", + "uid": "GETH-2020-06", + "introduced": "v1.9.4", + "fixed": "v1.9.20", + "summary": "A consensus-vulnerability in Geth could cause a chain split, where vulnerable versions refuse to accept the canonical chain.", + "description": "A flaw was repoted at 2020-08-11 by John Youngseok Yang (Software Platform Lab), where a particular sequence of transactions could cause a consensus failure.\n\n- Tx 1:\n - `sender` invokes `caller`.\n - `caller` invokes `0xaa`. `0xaa` has 3 wei, does a self-destruct-to-self\n - `caller` does a `1 wei` -call to `0xaa`, who thereby has 1 wei (the code in `0xaa` still executed, since the tx is still ongoing, but doesn't redo the selfdestruct, it takes a different path if callvalue is non-zero)\n\n-Tx 2:\n - `sender` does a 5-wei call to 0xaa. No exec (since no code). \n\nIn geth, the result would be that `0xaa` had `6 wei`, whereas OE reported (correctly) `5` wei. Furthermore, in geth, if the second tx was not executed, the `0xaa` would be destructed, resulting in `0 wei`. Thus obviously wrong. \n\nIt was determined that the root cause was this [commit](https://github.com/ethereum/go-ethereum/commit/223b950944f494a5b4e0957fd9f92c48b09037ad) from [this PR](https://github.com/ethereum/go-ethereum/pull/19953). The semantics of `createObject` was subtly changd, into returning a non-nil object (with `deleted=true`) where it previously did not if the account had been destructed. This return value caused the new object to inherit the old `balance`.\n", + "links": [ + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-xw37-57qp-9mm4" + ], + "published": "2020-12-10", + "severity": "High", + "CVE": "CVE-2020-26265", + "check": "(Geth\\/v1\\.9\\.(4|5|6|7|8|9)-.*)|(Geth\\/v1\\.9\\.1\\d-.*)$" + }, + { + "name": "Not ready for London upgrade", + "uid": "GETH-2021-01", + "summary": "The client is not ready for the 'London' technical upgrade, and will deviate from the canonical chain when the London upgrade occurs (at block '12965000' around August 4, 2021.", + "description": "At (or around) August 4, Ethereum will undergo a technical upgrade called 'London'. Clients not upgraded will fail to progress on the canonical chain.", + "links": [ + "https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md", + "https://notes.ethereum.org/@timbeiko/ropsten-postmortem" + ], + "introduced": "v1.10.1", + "fixed": "v1.10.6", + "published": "2021-07-22", + "severity": "High", + "check": "(Geth\\/v1\\.10\\.(1|2|3|4|5)-.*)$" + }, + { + "name": "RETURNDATA corruption via datacopy", + "uid": "GETH-2021-02", + "summary": "A consensus-flaw in the Geth EVM could cause a node to deviate from the canonical chain.", + "description": "A memory-corruption bug within the EVM can cause a consensus error, where vulnerable nodes obtain a different `stateRoot` when processing a maliciously crafted transaction. This, in turn, would lead to the chain being split: mainnet splitting in two forks.\n\nAll Geth versions supporting the London hard fork are vulnerable (the bug is older than London), so all users should update.\n\nThis bug was exploited on Mainnet at block 13107518.\n\nCredits for the discovery go to @guidovranken (working for Sentnl during an audit of the Telos EVM) and reported via bounty@ethereum.org.", + "links": [ + "https://github.com/ethereum/go-ethereum/blob/master/docs/postmortems/2021-08-22-split-postmortem.md", + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-9856-9gg9-qcmq", + "https://github.com/ethereum/go-ethereum/releases/tag/v1.10.8" + ], + "introduced": "v1.10.0", + "fixed": "v1.10.8", + "published": "2021-08-24", + "severity": "High", + "CVE": "CVE-2021-39137", + "check": "(Geth\\/v1\\.10\\.(0|1|2|3|4|5|6|7)-.*)$" + }, + { + "name": "DoS via malicious `snap/1` request", + "uid": "GETH-2021-03", + "summary": "A vulnerable node is susceptible to crash when processing a maliciously crafted message from a peer, via the snap/1 protocol. The crash can be triggered by sending a malicious snap/1 GetTrieNodes package.", + "description": "The `snap/1` protocol handler contains two vulnerabilities related to the `GetTrieNodes` packet, which can be exploited to crash the node. Full details are available at the Github security [advisory](https://github.com/ethereum/go-ethereum/security/advisories/GHSA-59hh-656j-3p7v)", + "links": [ + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-59hh-656j-3p7v", + "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities", + "https://github.com/ethereum/go-ethereum/pull/23657" + ], + "introduced": "v1.10.0", + "fixed": "v1.10.9", + "published": "2021-10-24", + "severity": "Medium", + "CVE": "CVE-2021-41173", + "check": "(Geth\\/v1\\.10\\.(0|1|2|3|4|5|6|7|8)-.*)$" + }, + { + "name": "DoS via malicious p2p message", + "uid": "GETH-2022-01", + "summary": "A vulnerable node can crash via p2p messages sent from an attacker node, if running with non-default log options.", + "description": "A vulnerable node, if configured to use high verbosity logging, can be made to crash when handling specially crafted p2p messages sent from an attacker node. Full details are available at the Github security [advisory](https://github.com/ethereum/go-ethereum/security/advisories/GHSA-wjxw-gh3m-7pm5)", + "links": [ + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-wjxw-gh3m-7pm5", + "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities", + "https://github.com/ethereum/go-ethereum/pull/24507" + ], + "introduced": "v1.10.0", + "fixed": "v1.10.17", + "published": "2022-05-11", + "severity": "Low", + "CVE": "CVE-2022-29177", + "check": "(Geth\\/v1\\.10\\.(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16)-.*)$" + }, + { + "name": "DoS via malicious p2p message", + "uid": "GETH-2023-01", + "summary": "A vulnerable node can be made to consume unbounded amounts of memory when handling specially crafted p2p messages sent from an attacker node.", + "description": "The p2p handler spawned a new goroutine to respond to ping requests. By flooding a node with ping requests, an unbounded number of goroutines can be created, leading to resource exhaustion and potentially crash due to OOM.", + "links": [ + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-ppjg-v974-84cm", + "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities" + ], + "introduced": "v1.10.0", + "fixed": "v1.12.1", + "published": "2023-09-06", + "severity": "High", + "CVE": "CVE-2023-40591", + "check": "(Geth\\/v1\\.(10|11)\\..*)|(Geth\\/v1\\.12\\.0-.*)$" + }, + { + "name": "DoS via malicious p2p message", + "uid": "GETH-2024-01", + "summary": "A vulnerable node can be made to consume very large amounts of memory when handling specially crafted p2p messages sent from an attacker node.", + "description": "A vulnerable node can be made to consume very large amounts of memory when handling specially crafted p2p messages sent from an attacker node. Full details will be available at the Github security [advisory](https://github.com/ethereum/go-ethereum/security/advisories/GHSA-4xc9-8hmq-j652)", + "links": [ + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-4xc9-8hmq-j652", + "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities" + ], + "introduced": "v1.10.0", + "fixed": "v1.13.15", + "published": "2024-05-06", + "severity": "High", + "CVE": "CVE-2024-32972", + "check": "(Geth\\/v1\\.(10|11|12)\\..*)|(Geth\\/v1\\.13\\.\\d-.*)|(Geth\\/v1\\.13\\.1(0|1|2|3|4)-.*)$" } ] diff --git a/cmd/geth/testdata/vcheck/minisig-sigs-new/data.json.minisig b/cmd/geth/testdata/vcheck/minisig-sigs-new/data.json.minisig index eaea9f9053..987ffe92bb 100644 --- a/cmd/geth/testdata/vcheck/minisig-sigs-new/data.json.minisig +++ b/cmd/geth/testdata/vcheck/minisig-sigs-new/data.json.minisig @@ -1,4 +1,4 @@ untrusted comment: signature from minisign secret key -RUQkliYstQBOKLK05Sy5f3bVRMBqJT26ABo6Vbp3BNJAVjejoqYCu4GWE/+7qcDfHBqYIniDCbFIUvYEnOHxV6vZ93wO1xJWDQw= -trusted comment: timestamp:1693986492 file:data.json hashed -6Fdw2H+W1ZXK7QXSF77Z5AWC7+AEFAfDmTSxNGylU5HLT1AuSJQmxslj+VjtUBamYCvOuET7plbXza942AlWDw== +RUQkliYstQBOKHklFEYCUjepz81dyUuDmIAxjAvXa+icjGuKcjtVfV06G7qfOMSpplS5EcntU12n+AnGNyuOM8zIctaIWcfG2w0= +trusted comment: timestamp:1752094689 file:data.json hashed +u2e4wo4HBTU6viQTSY/NVBHoWoPFJnnTvLZS0FYl3JdvSOYi6+qpbEsDhAIFqq/n8VmlS/fPqqf7vKCNiAgjAA== diff --git a/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1 b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1 index f9066d4fe0..6b6aa900e3 100644 --- a/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1 +++ b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1 @@ -1,4 +1,4 @@ untrusted comment: signature from minisign secret key -RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= -trusted comment: timestamp:1605618622 file:vulnerabilities.json -osAPs4QPdDkmiWQxqeMIzYv/b+ZGxJ+19Sbrk1Cpq4t2gHBT+lqFtwL3OCzKWWyjGRTmHfsVGBYpzEdPRQ0/BQ== +RWQkliYstQBOKNoyq2O98hPmeVJQ6ShQLM58+4n0gkY0y0trFMDAsHuN/l4IyHfh8dDQ1ry0+IuZVrf/i8M/P3YFzFfAymDYCQ0= +trusted comment: timestamp:1752094703 file:data.json +cNyq3ZGlqo785HtWODb9ejWqF0HhSeXuLGXzC7z1IhnDrBObWBJngYd3qBG1dQcYlHQ+bgB/On5mSyMFn4UoCQ== diff --git a/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2 b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2 index a89a83d21a..704437de39 100644 --- a/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2 +++ b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2 @@ -1,4 +1,4 @@ untrusted comment: Here's a comment -RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= +RWQkliYstQBOKNoyq2O98hPmeVJQ6ShQLM58+4n0gkY0y0trFMDAsHuN/l4IyHfh8dDQ1ry0+IuZVrf/i8M/P3YFzFfAymDYCQ0= trusted comment: Here's a trusted comment -3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ== +dL7lO8sqFFCOXJO/u8SgoDk2nlXGWPRDbOTJkChMbmtUp9PB7sG831basXkZ/0CQ/l/vG7AbPyMNEVZyJn5NCg== diff --git a/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3 b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3 index 6fd33b19a3..806cd07316 100644 --- a/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3 +++ b/cmd/geth/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3 @@ -1,4 +1,4 @@ -untrusted comment: One more (untrusted) comment -RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= +untrusted comment: One more (untrusted™) comment +RWQkliYstQBOKNoyq2O98hPmeVJQ6ShQLM58+4n0gkY0y0trFMDAsHuN/l4IyHfh8dDQ1ry0+IuZVrf/i8M/P3YFzFfAymDYCQ0= trusted comment: Here's a trusted comment -3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ== +dL7lO8sqFFCOXJO/u8SgoDk2nlXGWPRDbOTJkChMbmtUp9PB7sG831basXkZ/0CQ/l/vG7AbPyMNEVZyJn5NCg== diff --git a/cmd/geth/testdata/vcheck/signify-sigs/data.json.sig b/cmd/geth/testdata/vcheck/signify-sigs/data.json.sig index 3d5fcacf9a..d704af7709 100644 --- a/cmd/geth/testdata/vcheck/signify-sigs/data.json.sig +++ b/cmd/geth/testdata/vcheck/signify-sigs/data.json.sig @@ -1,2 +1,2 @@ -untrusted comment: verify with ./signifykey.pub -RWSKLNhZb0KdAbhRUhW2LQZXdnwttu2SYhM9EuC4mMgOJB85h7/YIPupf8/ldTs4N8e9Y/fhgdY40q5LQpt5IFC62fq0v8U1/w8= +untrusted comment: verify with signifykey.pub +RWSKLNhZb0KdARbMcGN40hbHzKQYZDgDOFhEUT1YpzMnqre/mbKJ8td/HVlG03Am1YCszATiI0DbnljjTy4iNHYwqBfzrFUqUg0= diff --git a/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.1 b/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.1 deleted file mode 100644 index f9066d4fe0..0000000000 --- a/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.1 +++ /dev/null @@ -1,4 +0,0 @@ -untrusted comment: signature from minisign secret key -RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= -trusted comment: timestamp:1605618622 file:vulnerabilities.json -osAPs4QPdDkmiWQxqeMIzYv/b+ZGxJ+19Sbrk1Cpq4t2gHBT+lqFtwL3OCzKWWyjGRTmHfsVGBYpzEdPRQ0/BQ== diff --git a/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.2 b/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.2 deleted file mode 100644 index a89a83d21a..0000000000 --- a/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.2 +++ /dev/null @@ -1,4 +0,0 @@ -untrusted comment: Here's a comment -RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= -trusted comment: Here's a trusted comment -3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ== diff --git a/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.3 b/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.3 deleted file mode 100644 index 6fd33b19a3..0000000000 --- a/cmd/geth/testdata/vcheck/sigs/vulnerabilities.json.minisig.3 +++ /dev/null @@ -1,4 +0,0 @@ -untrusted comment: One more (untrusted) comment -RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0= -trusted comment: Here's a trusted comment -3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ== diff --git a/cmd/geth/testdata/vcheck/vulnerabilities.json b/cmd/geth/testdata/vcheck/vulnerabilities.json index 31a34de6be..e52fd84e67 100644 --- a/cmd/geth/testdata/vcheck/vulnerabilities.json +++ b/cmd/geth/testdata/vcheck/vulnerabilities.json @@ -6,7 +6,7 @@ "description": "A mining flaw could cause miners to erroneously calculate PoW, due to an index overflow, if DAG size is exceeding the maximum 32 bit unsigned value.\n\nThis occurred on the ETC chain on 2020-11-06. This is likely to trigger for ETH mainnet around block `11550000`/epoch `385`, slated to occur early January 2021.\n\nThis issue is relevant only for miners, non-mining nodes are unaffected, since non-mining nodes use a smaller verification cache instead of a full DAG.", "links": [ "https://github.com/ethereum/go-ethereum/pull/21793", - "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://blog.ethereum.org/2020/11/12/geth-security-release", "https://github.com/ethereum/go-ethereum/commit/567d41d9363706b4b13ce0903804e8acf214af49", "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-v592-xf75-856p" ], @@ -23,7 +23,7 @@ "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing, due to an underlying bug in Go (CVE-2020-28362) versions < `1.15.5`, or `<1.14.12`", "description": "The DoS issue can be used to crash all Geth nodes during block processing, the effects of which would be that a major part of the Ethereum network went offline.\n\nOutside of Go-Ethereum, the issue is most likely relevant for all forks of Geth (such as TurboGeth or ETC’s core-geth) which is built with versions of Go which contains the vulnerability.", "links": [ - "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://blog.ethereum.org/2020/11/12/geth-security-release", "https://groups.google.com/g/golang-announce/c/NpBGTTmKzpM", "https://github.com/golang/go/issues/42552", "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-m6gx-rhvj-fh52" @@ -41,7 +41,7 @@ "summary": "A consensus flaw in Geth, related to `datacopy` precompile", "description": "Geth erroneously performed a 'shallow' copy when the precompiled `datacopy` (at `0x00...04`) was invoked. An attacker could deploy a contract that uses the shallow copy to corrupt the contents of the `RETURNDATA`, thus causing a consensus failure.", "links": [ - "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://blog.ethereum.org/2020/11/12/geth-security-release", "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-69v6-xc2j-r2jf" ], "introduced": "v1.9.7", @@ -57,7 +57,7 @@ "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing", "description": "Affected versions suffer from a vulnerability which can be exploited through the `MULMOD` operation, by specifying a modulo of `0`: `mulmod(a,b,0)`, causing a `panic` in the underlying library. \nThe crash was in the `uint256` library, where a buffer [underflowed](https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L442).\n\n\tif `d == 0`, `dLen` remains `0`\n\nand https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L451 will try to access index `[-1]`.\n\nThe `uint256` library was first merged in this [commit](https://github.com/ethereum/go-ethereum/commit/cf6674539c589f80031f3371a71c6a80addbe454), on 2020-06-08. \nExploiting this vulnerabilty would cause all vulnerable nodes to drop off the network. \n\nThe issue was brought to our attention through a [bug report](https://github.com/ethereum/go-ethereum/issues/21367), showing a `panic` occurring on sync from genesis on the Ropsten network.\n \nIt was estimated that the least obvious way to fix this would be to merge the fix into `uint256`, make a new release of that library and then update the geth-dependency.\n", "links": [ - "https://blog.ethereum.org/2020/11/12/geth_security_release/", + "https://blog.ethereum.org/2020/11/12/geth-security-release", "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-jm5c-rv3w-w83m", "https://github.com/holiman/uint256/releases/tag/v1.1.1", "https://github.com/holiman/uint256/pull/80", diff --git a/cmd/geth/version_check_test.go b/cmd/geth/version_check_test.go index 34171cb035..fb5d1b2d69 100644 --- a/cmd/geth/version_check_test.go +++ b/cmd/geth/version_check_test.go @@ -36,6 +36,9 @@ func TestVerification(t *testing.T) { t.Parallel() // For this test, the pubkey is in testdata/vcheck/minisign.pub // (the privkey is `minisign.sec`, if we want to expand this test. Password 'test' ) + // 1. `minisign -S -l -s ./minisign.sec -m data.json -x ./minisig-sigs/vulnerabilities.json.minisig.1 -c "signature from minisign secret key"` + // 2. `minisign -S -l -s ./minisign.sec -m vulnerabilities.json -x ./minisig-sigs/vulnerabilities.json.minisig.2 -c "Here's a comment" -t "Here's a trusted comment"` + // 3. minisign -S -l -s ./minisign.sec -m vulnerabilities.json -x ./minisig-sigs/vulnerabilities.json.minisig.3 -c "One more (untrusted™) comment" -t "Here's a trusted comment" pub := "RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp" testVerification(t, pub, "./testdata/vcheck/minisig-sigs/") }) @@ -43,7 +46,7 @@ func TestVerification(t *testing.T) { t.Parallel() // For this test, the pubkey is in testdata/vcheck/minisign.pub // (the privkey is `minisign.sec`, if we want to expand this test. Password 'test' ) - // `minisign -S -s ./minisign.sec -m data.json -x ./minisig-sigs-new/data.json.minisig` + // `minisign -S -s ./minisign.sec -m data.json -x ./minisig-sigs-new/data.json.minisig` pub := "RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp" testVerification(t, pub, "./testdata/vcheck/minisig-sigs-new/") }) @@ -53,6 +56,7 @@ func TestVerification(t *testing.T) { t.Skip("This currently fails, minisign expects 4 lines of data, signify provides only 2") // For this test, the pubkey is in testdata/vcheck/signifykey.pub // (the privkey is `signifykey.sec`, if we want to expand this test. Password 'test' ) + // `signify -S -s signifykey.sec -m data.json -x ./signify-sigs/data.json.sig` pub := "RWSKLNhZb0KdATtRT7mZC/bybI3t3+Hv/O2i3ye04Dq9fnT9slpZ1a2/" testVerification(t, pub, "./testdata/vcheck/signify-sigs/") }) From 055e1e6291e8420eeec53a81664a36cddbc64246 Mon Sep 17 00:00:00 2001 From: maskpp Date: Mon, 14 Jul 2025 15:07:47 +0800 Subject: [PATCH 05/56] signer/core/apitypes: require blob txs to have tx.to set (#32197) Check the `to` address before building the blob tx. --------- Co-authored-by: jwasinger --- signer/core/apitypes/types.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index b8b96bef92..9a32312d46 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -150,6 +150,9 @@ func (args *SendTxArgs) ToTransaction() (*types.Transaction, error) { if args.AccessList != nil { al = *args.AccessList } + if to == nil { + return nil, fmt.Errorf("transaction recipient must be set for blob transactions") + } data = &types.BlobTx{ To: *to, ChainID: uint256.MustFromBig((*big.Int)(args.ChainID)), From a9061cfd77a26634d459f824793335ea73be14da Mon Sep 17 00:00:00 2001 From: kilavvy <140459108+kilavvy@users.noreply.github.com> Date: Mon, 14 Jul 2025 09:15:18 +0200 Subject: [PATCH 06/56] accounts/keystore: update links to documenation (#32194) --- **Description:** - Replaced outdated GitHub wiki links with the official Ethereum documentation for Web3 Secret Storage. - Updated references in `keystore.go` and `passphrase.go` for improved accuracy and reliability. --- --- accounts/keystore/keystore.go | 2 +- accounts/keystore/passphrase.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go index df3dda60b6..3e85b0433b 100644 --- a/accounts/keystore/keystore.go +++ b/accounts/keystore/keystore.go @@ -17,7 +17,7 @@ // Package keystore implements encrypted storage of secp256k1 private keys. // // Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification. -// See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information. +// See https://ethereum.org/en/developers/docs/data-structures-and-encoding/web3-secret-storage/ for more information. package keystore import ( diff --git a/accounts/keystore/passphrase.go b/accounts/keystore/passphrase.go index e7a7f8d0cb..fc7ea938e2 100644 --- a/accounts/keystore/passphrase.go +++ b/accounts/keystore/passphrase.go @@ -19,7 +19,7 @@ This key store behaves as KeyStorePlain with the difference that the private key is encrypted and on disk uses another JSON encoding. -The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition +The crypto is documented at https://ethereum.org/en/developers/docs/data-structures-and-encoding/web3-secret-storage/ */ From 90a098904f552ee722b0d0d5eccb3500d90a85a8 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 14 Jul 2025 11:27:42 +0200 Subject: [PATCH 07/56] ethclient/gethclient: remove race condition in tests (#32206) alternative to https://github.com/ethereum/go-ethereum/pull/32200 The race condition is not happening yet, since there is only a single call to `newTestBackend`, but there might be more in the future --- ethclient/gethclient/gethclient_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go index 02635faabc..0eed63cacf 100644 --- a/ethclient/gethclient/gethclient_test.go +++ b/ethclient/gethclient/gethclient_test.go @@ -48,12 +48,11 @@ var ( testSlot = common.HexToHash("0xdeadbeef") testValue = crypto.Keccak256Hash(testSlot[:]) testBalance = big.NewInt(2e15) - testTxHashes []common.Hash ) -func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { +func newTestBackend(t *testing.T) (*node.Node, []*types.Block, []common.Hash) { // Generate test chain. - genesis, blocks := generateTestChain() + genesis, blocks, txHashes := generateTestChain() // Create node n, err := node.New(&node.Config{ HTTPModules: []string{"debug", "eth", "admin"}, @@ -82,10 +81,10 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil { t.Fatalf("can't import test blocks: %v", err) } - return n, blocks + return n, blocks, txHashes } -func generateTestChain() (*core.Genesis, []*types.Block) { +func generateTestChain() (*core.Genesis, []*types.Block, []common.Hash) { genesis := &core.Genesis{ Config: params.AllEthashProtocolChanges, Alloc: types.GenesisAlloc{ @@ -96,6 +95,7 @@ func generateTestChain() (*core.Genesis, []*types.Block) { ExtraData: []byte("test genesis"), Timestamp: 9000, } + txHashes := make([]common.Hash, 0) generate := func(i int, g *core.BlockGen) { g.OffsetTime(5) g.SetExtra([]byte("test")) @@ -111,15 +111,15 @@ func generateTestChain() (*core.Genesis, []*types.Block) { }) tx, _ = types.SignTx(tx, types.LatestSignerForChainID(genesis.Config.ChainID), testKey) g.AddTx(tx) - testTxHashes = append(testTxHashes, tx.Hash()) + txHashes = append(txHashes, tx.Hash()) } _, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 1, generate) blocks = append([]*types.Block{genesis.ToBlock()}, blocks...) - return genesis, blocks + return genesis, blocks, txHashes } func TestGethClient(t *testing.T) { - backend, _ := newTestBackend(t) + backend, _, txHashes := newTestBackend(t) client := backend.Attach() defer backend.Close() defer client.Close() @@ -172,7 +172,7 @@ func TestGethClient(t *testing.T) { }, { "TestTraceTransaction", - func(t *testing.T) { testTraceTransactions(t, client) }, + func(t *testing.T) { testTraceTransactions(t, client, txHashes) }, }, { "TestSetHead", @@ -480,9 +480,9 @@ func testCallContract(t *testing.T, client *rpc.Client) { } } -func testTraceTransactions(t *testing.T, client *rpc.Client) { +func testTraceTransactions(t *testing.T, client *rpc.Client, txHashes []common.Hash) { ec := New(client) - for _, txHash := range testTxHashes { + for _, txHash := range txHashes { // Struct logger _, err := ec.TraceTransaction(context.Background(), txHash, nil) if err != nil { From a327ffe9b35289719ac3c484b7332584985b598a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 14 Jul 2025 14:07:43 +0200 Subject: [PATCH 08/56] params: EIP-7892 - Blob Parameter Only Hardforks (#32193) This is a resubmit of https://github.com/ethereum/go-ethereum/pull/31820 against the `master` branch. --------- Co-authored-by: Marius van der Wijden Co-authored-by: Gary Rong --- consensus/misc/eip4844/eip4844.go | 72 ++++++++++++++++------------- params/config.go | 75 +++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 32 deletions(-) diff --git a/consensus/misc/eip4844/eip4844.go b/consensus/misc/eip4844/eip4844.go index 049043e3ce..fc143027dd 100644 --- a/consensus/misc/eip4844/eip4844.go +++ b/consensus/misc/eip4844/eip4844.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/params/forks" ) var ( @@ -100,42 +99,53 @@ func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTim // CalcBlobFee calculates the blobfee from the header's excess blob gas field. func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int { - var frac uint64 - switch config.LatestFork(header.Time) { - case forks.Osaka: - frac = config.BlobScheduleConfig.Osaka.UpdateFraction - case forks.Prague: - frac = config.BlobScheduleConfig.Prague.UpdateFraction - case forks.Cancun: - frac = config.BlobScheduleConfig.Cancun.UpdateFraction - default: + blobConfig := latestBlobConfig(config, header.Time) + if blobConfig == nil { panic("calculating blob fee on unsupported fork") } - return fakeExponential(minBlobGasPrice, new(big.Int).SetUint64(*header.ExcessBlobGas), new(big.Int).SetUint64(frac)) + return fakeExponential(minBlobGasPrice, new(big.Int).SetUint64(*header.ExcessBlobGas), new(big.Int).SetUint64(blobConfig.UpdateFraction)) } // MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp. func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int { - if cfg.BlobScheduleConfig == nil { + blobConfig := latestBlobConfig(cfg, time) + if blobConfig == nil { return 0 } + return blobConfig.Max +} + +func latestBlobConfig(cfg *params.ChainConfig, time uint64) *params.BlobConfig { + if cfg.BlobScheduleConfig == nil { + return nil + } var ( london = cfg.LondonBlock s = cfg.BlobScheduleConfig ) switch { + case cfg.IsBPO5(london, time) && s.BPO5 != nil: + return s.BPO5 + case cfg.IsBPO4(london, time) && s.BPO4 != nil: + return s.BPO4 + case cfg.IsBPO3(london, time) && s.BPO3 != nil: + return s.BPO3 + case cfg.IsBPO2(london, time) && s.BPO2 != nil: + return s.BPO2 + case cfg.IsBPO1(london, time) && s.BPO1 != nil: + return s.BPO1 case cfg.IsOsaka(london, time) && s.Osaka != nil: - return s.Osaka.Max + return s.Osaka case cfg.IsPrague(london, time) && s.Prague != nil: - return s.Prague.Max + return s.Prague case cfg.IsCancun(london, time) && s.Cancun != nil: - return s.Cancun.Max + return s.Cancun default: - return 0 + return nil } } -// MaxBlobsPerBlock returns the maximum blob gas that can be spent in a block at the given timestamp. +// MaxBlobGasPerBlock returns the maximum blob gas that can be spent in a block at the given timestamp. func MaxBlobGasPerBlock(cfg *params.ChainConfig, time uint64) uint64 { return uint64(MaxBlobsPerBlock(cfg, time)) * params.BlobTxBlobGasPerBlob } @@ -148,6 +158,16 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int { return 0 } switch { + case s.BPO5 != nil: + return s.BPO5.Max + case s.BPO4 != nil: + return s.BPO4.Max + case s.BPO3 != nil: + return s.BPO3.Max + case s.BPO2 != nil: + return s.BPO2.Max + case s.BPO1 != nil: + return s.BPO1.Max case s.Osaka != nil: return s.Osaka.Max case s.Prague != nil: @@ -161,23 +181,11 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int { // targetBlobsPerBlock returns the target number of blobs in a block at the given timestamp. func targetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int { - if cfg.BlobScheduleConfig == nil { - return 0 - } - var ( - london = cfg.LondonBlock - s = cfg.BlobScheduleConfig - ) - switch { - case cfg.IsOsaka(london, time) && s.Osaka != nil: - return s.Osaka.Target - case cfg.IsPrague(london, time) && s.Prague != nil: - return s.Prague.Target - case cfg.IsCancun(london, time) && s.Cancun != nil: - return s.Cancun.Target - default: + blobConfig := latestBlobConfig(cfg, time) + if blobConfig == nil { return 0 } + return blobConfig.Target } // fakeExponential approximates factor * e ** (numerator / denominator) using diff --git a/params/config.go b/params/config.go index 58a0550303..85619bbe22 100644 --- a/params/config.go +++ b/params/config.go @@ -411,6 +411,11 @@ type ChainConfig struct { PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague) OsakaTime *uint64 `json:"osakaTime,omitempty"` // Osaka switch time (nil = no fork, 0 = already on osaka) VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle) + BPO1Time *uint64 `json:"bpo1Time,omitempty"` // BPO1 switch time (nil = no fork, 0 = already on bpo1) + BPO2Time *uint64 `json:"bpo2Time,omitempty"` // BPO2 switch time (nil = no fork, 0 = already on bpo2) + BPO3Time *uint64 `json:"bpo3Time,omitempty"` // BPO3 switch time (nil = no fork, 0 = already on bpo3) + BPO4Time *uint64 `json:"bpo4Time,omitempty"` // BPO4 switch time (nil = no fork, 0 = already on bpo4) + BPO5Time *uint64 `json:"bpo5Time,omitempty"` // BPO5 switch time (nil = no fork, 0 = already on bpo5) // TerminalTotalDifficulty is the amount of total difficulty reached by // the network that triggers the consensus upgrade. @@ -531,6 +536,21 @@ func (c *ChainConfig) Description() string { if c.VerkleTime != nil { banner += fmt.Sprintf(" - Verkle: @%-10v\n", *c.VerkleTime) } + if c.BPO1Time != nil { + banner += fmt.Sprintf(" - BPO1: @%-10v\n", *c.BPO1Time) + } + if c.BPO2Time != nil { + banner += fmt.Sprintf(" - BPO2: @%-10v\n", *c.BPO2Time) + } + if c.BPO3Time != nil { + banner += fmt.Sprintf(" - BPO3: @%-10v\n", *c.BPO3Time) + } + if c.BPO4Time != nil { + banner += fmt.Sprintf(" - BPO4: @%-10v\n", *c.BPO4Time) + } + if c.BPO5Time != nil { + banner += fmt.Sprintf(" - BPO5: @%-10v\n", *c.BPO5Time) + } return banner } @@ -547,6 +567,11 @@ type BlobScheduleConfig struct { Prague *BlobConfig `json:"prague,omitempty"` Osaka *BlobConfig `json:"osaka,omitempty"` Verkle *BlobConfig `json:"verkle,omitempty"` + BPO1 *BlobConfig `json:"bpo1,omitempty"` + BPO2 *BlobConfig `json:"bpo2,omitempty"` + BPO3 *BlobConfig `json:"bpo3,omitempty"` + BPO4 *BlobConfig `json:"bpo4,omitempty"` + BPO5 *BlobConfig `json:"bpo5,omitempty"` } // IsHomestead returns whether num is either equal to the homestead block or greater. @@ -654,6 +679,31 @@ func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time) } +// IsBPO1 returns whether time is either equal to the BPO1 fork time or greater. +func (c *ChainConfig) IsBPO1(num *big.Int, time uint64) bool { + return c.IsLondon(num) && isTimestampForked(c.BPO1Time, time) +} + +// IsBPO2 returns whether time is either equal to the BPO2 fork time or greater. +func (c *ChainConfig) IsBPO2(num *big.Int, time uint64) bool { + return c.IsLondon(num) && isTimestampForked(c.BPO2Time, time) +} + +// IsBPO3 returns whether time is either equal to the BPO3 fork time or greater. +func (c *ChainConfig) IsBPO3(num *big.Int, time uint64) bool { + return c.IsLondon(num) && isTimestampForked(c.BPO3Time, time) +} + +// IsBPO4 returns whether time is either equal to the BPO4 fork time or greater. +func (c *ChainConfig) IsBPO4(num *big.Int, time uint64) bool { + return c.IsLondon(num) && isTimestampForked(c.BPO4Time, time) +} + +// IsBPO5 returns whether time is either equal to the BPO5 fork time or greater. +func (c *ChainConfig) IsBPO5(num *big.Int, time uint64) bool { + return c.IsLondon(num) && isTimestampForked(c.BPO5Time, time) +} + // IsVerkleGenesis checks whether the verkle fork is activated at the genesis block. // // Verkle mode is considered enabled if the verkle fork time is configured, @@ -729,6 +779,11 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "pragueTime", timestamp: c.PragueTime, optional: true}, {name: "osakaTime", timestamp: c.OsakaTime, optional: true}, {name: "verkleTime", timestamp: c.VerkleTime, optional: true}, + {name: "bpo1", timestamp: c.BPO1Time, optional: true}, + {name: "bpo2", timestamp: c.BPO2Time, optional: true}, + {name: "bpo3", timestamp: c.BPO3Time, optional: true}, + {name: "bpo4", timestamp: c.BPO4Time, optional: true}, + {name: "bpo5", timestamp: c.BPO5Time, optional: true}, } { if lastFork.name != "" { switch { @@ -778,6 +833,11 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "cancun", timestamp: c.CancunTime, config: bsc.Cancun}, {name: "prague", timestamp: c.PragueTime, config: bsc.Prague}, {name: "osaka", timestamp: c.OsakaTime, config: bsc.Osaka}, + {name: "bpo1", timestamp: c.BPO1Time, config: bsc.BPO1}, + {name: "bpo2", timestamp: c.BPO2Time, config: bsc.BPO2}, + {name: "bpo3", timestamp: c.BPO3Time, config: bsc.BPO3}, + {name: "bpo4", timestamp: c.BPO4Time, config: bsc.BPO4}, + {name: "bpo5", timestamp: c.BPO5Time, config: bsc.BPO5}, } { if cur.config != nil { if err := cur.config.validate(); err != nil { @@ -878,6 +938,21 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, if isForkTimestampIncompatible(c.VerkleTime, newcfg.VerkleTime, headTimestamp) { return newTimestampCompatError("Verkle fork timestamp", c.VerkleTime, newcfg.VerkleTime) } + if isForkTimestampIncompatible(c.BPO1Time, newcfg.BPO1Time, headTimestamp) { + return newTimestampCompatError("BPO1 fork timestamp", c.BPO1Time, newcfg.BPO1Time) + } + if isForkTimestampIncompatible(c.BPO2Time, newcfg.BPO2Time, headTimestamp) { + return newTimestampCompatError("BPO2 fork timestamp", c.BPO2Time, newcfg.BPO2Time) + } + if isForkTimestampIncompatible(c.BPO3Time, newcfg.BPO3Time, headTimestamp) { + return newTimestampCompatError("BPO3 fork timestamp", c.BPO3Time, newcfg.BPO3Time) + } + if isForkTimestampIncompatible(c.BPO4Time, newcfg.BPO4Time, headTimestamp) { + return newTimestampCompatError("BPO4 fork timestamp", c.BPO4Time, newcfg.BPO4Time) + } + if isForkTimestampIncompatible(c.BPO5Time, newcfg.BPO5Time, headTimestamp) { + return newTimestampCompatError("BPO5 fork timestamp", c.BPO5Time, newcfg.BPO5Time) + } return nil } From e9e12a97d27ccaa3a11b80f03b897a515ed44aa2 Mon Sep 17 00:00:00 2001 From: Bosul Mun Date: Mon, 14 Jul 2025 15:33:24 +0200 Subject: [PATCH 09/56] eth/fetcher: fix announcement drop logic (#32210) This PR fixes an issue in the tx_fetcher DoS prevention logic where the code keeps the overflow amount (`want - maxTxAnnounces`) instead of the allowed amount (`maxTxAnnounces - used`). The specific changes are: - Correct slice indexing in the announcement drop logic - Extend the overflow test case to cover the inversion scenario --- eth/fetcher/tx_fetcher.go | 4 ++-- eth/fetcher/tx_fetcher_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/eth/fetcher/tx_fetcher.go b/eth/fetcher/tx_fetcher.go index 98a1c6e9a6..3e050320e9 100644 --- a/eth/fetcher/tx_fetcher.go +++ b/eth/fetcher/tx_fetcher.go @@ -439,8 +439,8 @@ func (f *TxFetcher) loop() { if want > maxTxAnnounces { txAnnounceDOSMeter.Mark(int64(want - maxTxAnnounces)) - ann.hashes = ann.hashes[:want-maxTxAnnounces] - ann.metas = ann.metas[:want-maxTxAnnounces] + ann.hashes = ann.hashes[:maxTxAnnounces-used] + ann.metas = ann.metas[:maxTxAnnounces-used] } // All is well, schedule the remainder of the transactions var ( diff --git a/eth/fetcher/tx_fetcher_test.go b/eth/fetcher/tx_fetcher_test.go index c4c8cac56e..0f05a1c995 100644 --- a/eth/fetcher/tx_fetcher_test.go +++ b/eth/fetcher/tx_fetcher_test.go @@ -1179,6 +1179,24 @@ func TestTransactionFetcherDoSProtection(t *testing.T) { size: 111, }) } + var ( + hashesC []common.Hash + typesC []byte + sizesC []uint32 + announceC []announce + ) + for i := 0; i < maxTxAnnounces+2; i++ { + hash := common.Hash{0x03, byte(i / 256), byte(i % 256)} + hashesC = append(hashesC, hash) + typesC = append(typesC, types.LegacyTxType) + sizesC = append(sizesC, 111) + + announceC = append(announceC, announce{ + hash: hash, + kind: types.LegacyTxType, + size: 111, + }) + } testTransactionFetcherParallel(t, txFetcherTest{ init: func() *TxFetcher { return NewTxFetcher( @@ -1192,43 +1210,52 @@ func TestTransactionFetcherDoSProtection(t *testing.T) { // Announce half of the transaction and wait for them to be scheduled doTxNotify{peer: "A", hashes: hashesA[:maxTxAnnounces/2], types: typesA[:maxTxAnnounces/2], sizes: sizesA[:maxTxAnnounces/2]}, doTxNotify{peer: "B", hashes: hashesB[:maxTxAnnounces/2-1], types: typesB[:maxTxAnnounces/2-1], sizes: sizesB[:maxTxAnnounces/2-1]}, + doTxNotify{peer: "C", hashes: hashesC[:maxTxAnnounces/2-1], types: typesC[:maxTxAnnounces/2-1], sizes: sizesC[:maxTxAnnounces/2-1]}, doWait{time: txArriveTimeout, step: true}, // Announce the second half and keep them in the wait list doTxNotify{peer: "A", hashes: hashesA[maxTxAnnounces/2 : maxTxAnnounces], types: typesA[maxTxAnnounces/2 : maxTxAnnounces], sizes: sizesA[maxTxAnnounces/2 : maxTxAnnounces]}, doTxNotify{peer: "B", hashes: hashesB[maxTxAnnounces/2-1 : maxTxAnnounces-1], types: typesB[maxTxAnnounces/2-1 : maxTxAnnounces-1], sizes: sizesB[maxTxAnnounces/2-1 : maxTxAnnounces-1]}, + doTxNotify{peer: "C", hashes: hashesC[maxTxAnnounces/2-1 : maxTxAnnounces-1], types: typesC[maxTxAnnounces/2-1 : maxTxAnnounces-1], sizes: sizesC[maxTxAnnounces/2-1 : maxTxAnnounces-1]}, // Ensure the hashes are split half and half isWaiting(map[string][]announce{ "A": announceA[maxTxAnnounces/2 : maxTxAnnounces], "B": announceB[maxTxAnnounces/2-1 : maxTxAnnounces-1], + "C": announceC[maxTxAnnounces/2-1 : maxTxAnnounces-1], }), isScheduled{ tracking: map[string][]announce{ "A": announceA[:maxTxAnnounces/2], "B": announceB[:maxTxAnnounces/2-1], + "C": announceC[:maxTxAnnounces/2-1], }, fetching: map[string][]common.Hash{ "A": hashesA[:maxTxRetrievals], "B": hashesB[:maxTxRetrievals], + "C": hashesC[:maxTxRetrievals], }, }, // Ensure that adding even one more hash results in dropping the hash doTxNotify{peer: "A", hashes: []common.Hash{hashesA[maxTxAnnounces]}, types: []byte{typesA[maxTxAnnounces]}, sizes: []uint32{sizesA[maxTxAnnounces]}}, doTxNotify{peer: "B", hashes: hashesB[maxTxAnnounces-1 : maxTxAnnounces+1], types: typesB[maxTxAnnounces-1 : maxTxAnnounces+1], sizes: sizesB[maxTxAnnounces-1 : maxTxAnnounces+1]}, + doTxNotify{peer: "C", hashes: hashesC[maxTxAnnounces-1 : maxTxAnnounces+2], types: typesC[maxTxAnnounces-1 : maxTxAnnounces+2], sizes: sizesC[maxTxAnnounces-1 : maxTxAnnounces+2]}, isWaiting(map[string][]announce{ "A": announceA[maxTxAnnounces/2 : maxTxAnnounces], "B": announceB[maxTxAnnounces/2-1 : maxTxAnnounces], + "C": announceC[maxTxAnnounces/2-1 : maxTxAnnounces], }), isScheduled{ tracking: map[string][]announce{ "A": announceA[:maxTxAnnounces/2], "B": announceB[:maxTxAnnounces/2-1], + "C": announceC[:maxTxAnnounces/2-1], }, fetching: map[string][]common.Hash{ "A": hashesA[:maxTxRetrievals], "B": hashesB[:maxTxRetrievals], + "C": hashesC[:maxTxRetrievals], }, }, }, From 1a5f399e301baf795d7d4c38d9a53ea5d7940a1b Mon Sep 17 00:00:00 2001 From: maskpp Date: Mon, 14 Jul 2025 22:06:57 +0800 Subject: [PATCH 10/56] miner: set sidecar version when recomputing proofs (#32199) - If the block number is `osaka` fork and needs to recompute some `blob proofs` to `cell proofs`, here also needs to set version to `1`. --- miner/worker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/miner/worker.go b/miner/worker.go index 799107f3bf..21e73058ce 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -440,6 +440,7 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran } sidecar.Proofs = append(sidecar.Proofs, cellProofs...) } + sidecar.Version = 1 } } } From 5bce990891435ff9881192cf52373bf93317b704 Mon Sep 17 00:00:00 2001 From: FT <140458077+zeevick10@users.noreply.github.com> Date: Tue, 15 Jul 2025 00:08:06 +0200 Subject: [PATCH 11/56] all: fix outdated ethereum wiki json-rpc json-rpc doc links (#32209) Replace outdated wiki reference with ethereum.org documentation links --- internal/ethapi/api.go | 2 +- internal/ethapi/errors.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 21c35b7665..8857f1c04f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1549,7 +1549,7 @@ func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil // // The account associated with addr must be unlocked. // -// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign +// https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign func (api *TransactionAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) { // Look up the wallet containing the requested signer account := accounts.Account{Address: addr} diff --git a/internal/ethapi/errors.go b/internal/ethapi/errors.go index ae38061234..154938fa0e 100644 --- a/internal/ethapi/errors.go +++ b/internal/ethapi/errors.go @@ -34,7 +34,7 @@ type revertError struct { } // ErrorCode returns the JSON error code for a revert. -// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal +// See: https://ethereum.org/en/developers/docs/apis/json-rpc/#error-codes func (e *revertError) ErrorCode() int { return 3 } @@ -71,7 +71,7 @@ func (e *TxIndexingError) Error() string { } // ErrorCode returns the JSON error code for a revert. -// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal +// See: https://ethereum.org/en/developers/docs/apis/json-rpc/#error-codes func (e *TxIndexingError) ErrorCode() int { return -32000 // to be decided } From fe0ae06c779bc62901dcd9f6fe8b1967409c1bf5 Mon Sep 17 00:00:00 2001 From: maskpp Date: Tue, 15 Jul 2025 09:07:23 +0800 Subject: [PATCH 12/56] core/types: fix CellProofsAt method (#32198) --- core/types/tx_blob.go | 24 +++++++++++++----------- eth/catalyst/api.go | 7 ++++++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index 93a76a28b7..774d6d14d2 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -22,7 +22,6 @@ import ( "errors" "fmt" "math/big" - "slices" "github.com/ethereum/go-ethereum/common" @@ -75,17 +74,19 @@ func (sc *BlobTxSidecar) BlobHashes() []common.Hash { } // CellProofsAt returns the cell proofs for blob with index idx. -func (sc *BlobTxSidecar) CellProofsAt(idx int) []kzg4844.Proof { - var cellProofs []kzg4844.Proof - for i := range kzg4844.CellProofsPerBlob { - index := idx*kzg4844.CellProofsPerBlob + i - if index > len(sc.Proofs) { - return nil - } - proof := sc.Proofs[index] - cellProofs = append(cellProofs, proof) +// This method is only valid for sidecars with version 1. +func (sc *BlobTxSidecar) CellProofsAt(idx int) ([]kzg4844.Proof, error) { + if sc.Version != 1 { + return nil, fmt.Errorf("cell proof unsupported, version: %d", sc.Version) } - return cellProofs + if idx < 0 || idx >= len(sc.Blobs) { + return nil, fmt.Errorf("cell proof out of bounds, index: %d, blobs: %d", idx, len(sc.Blobs)) + } + index := idx * kzg4844.CellProofsPerBlob + if len(sc.Proofs) < index+kzg4844.CellProofsPerBlob { + return nil, fmt.Errorf("cell proof is corrupted, index: %d, proofs: %d", idx, len(sc.Proofs)) + } + return sc.Proofs[index : index+kzg4844.CellProofsPerBlob], nil } // encodedSize computes the RLP size of the sidecar elements. This does NOT return the @@ -217,6 +218,7 @@ func (tx *BlobTx) copy() TxData { } if tx.Sidecar != nil { cpy.Sidecar = &BlobTxSidecar{ + Version: tx.Sidecar.Version, Blobs: slices.Clone(tx.Sidecar.Blobs), Commitments: slices.Clone(tx.Sidecar.Commitments), Proofs: slices.Clone(tx.Sidecar.Proofs), diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 409899d582..c52ffb09ba 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -564,7 +564,12 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo blobHashes := sidecar.BlobHashes() for bIdx, hash := range blobHashes { if idxes, ok := index[hash]; ok { - proofs := sidecar.CellProofsAt(bIdx) + proofs, err := sidecar.CellProofsAt(bIdx) + if err != nil { + // TODO @rjl @marius we should return an error + log.Info("Failed to get cell proof", "err", err) + return nil, nil + } var cellProofs []hexutil.Bytes for _, proof := range proofs { cellProofs = append(cellProofs, proof[:]) From 17903fedf0374be940f1e75260a8eacbbef62d0a Mon Sep 17 00:00:00 2001 From: Delweng Date: Tue, 15 Jul 2025 11:45:20 +0800 Subject: [PATCH 13/56] triedb/pathdb: introduce file-based state journal (#32060) Introduce file-based state journal in path database, fixing the Pebble restriction when the journal size exceeds 4GB. --------- Signed-off-by: jsvisa Co-authored-by: Gary Rong --- cmd/utils/flags.go | 6 ++ core/blockchain.go | 10 +-- core/rawdb/accessors_state.go | 8 --- eth/backend.go | 6 +- triedb/pathdb/database.go | 22 ++++++- triedb/pathdb/database_test.go | 52 +++++++++++----- triedb/pathdb/fileutils_unix.go | 57 +++++++++++++++++ triedb/pathdb/fileutils_windows.go | 25 ++++++++ triedb/pathdb/history_reader_test.go | 2 +- triedb/pathdb/journal.go | 93 +++++++++++++++++++++++++--- 10 files changed, 240 insertions(+), 41 deletions(-) create mode 100644 triedb/pathdb/fileutils_unix.go create mode 100644 triedb/pathdb/fileutils_windows.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index d3e842149a..b86970651f 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2198,6 +2198,12 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh StateHistory: ctx.Uint64(StateHistoryFlag.Name), // Disable transaction indexing/unindexing. TxLookupLimit: -1, + + // Enables file journaling for the trie database. The journal files will be stored + // within the data directory. The corresponding paths will be either: + // - DATADIR/triedb/merkle.journal + // - DATADIR/triedb/verkle.journal + TrieJournalDirectory: stack.ResolvePath("triedb"), } if options.ArchiveMode && !options.Preimages { options.Preimages = true diff --git a/core/blockchain.go b/core/blockchain.go index 2290b6d3cd..d52990ec5a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -162,10 +162,11 @@ const ( // BlockChainConfig contains the configuration of the BlockChain object. type BlockChainConfig struct { // Trie database related options - TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory - TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk - TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk - TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed + TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory + TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk + TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk + TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed + TrieJournalDirectory string // Directory path to the journal used for persisting trie data across node restarts Preimages bool // Whether to store preimage of trie key to the disk StateHistory uint64 // Number of blocks from head whose state histories are reserved. @@ -246,6 +247,7 @@ func (cfg *BlockChainConfig) triedbConfig(isVerkle bool) *triedb.Config { EnableStateIndexing: cfg.ArchiveMode, TrieCleanSize: cfg.TrieCleanLimit * 1024 * 1024, StateCleanSize: cfg.SnapshotLimit * 1024 * 1024, + JournalDirectory: cfg.TrieJournalDirectory, // TODO(rjl493456442): The write buffer represents the memory limit used // for flushing both trie data and state data to disk. The config name diff --git a/core/rawdb/accessors_state.go b/core/rawdb/accessors_state.go index 7d7b37641b..44f041d82e 100644 --- a/core/rawdb/accessors_state.go +++ b/core/rawdb/accessors_state.go @@ -157,14 +157,6 @@ func WriteTrieJournal(db ethdb.KeyValueWriter, journal []byte) { } } -// DeleteTrieJournal deletes the serialized in-memory trie nodes of layers saved at -// the last shutdown. -func DeleteTrieJournal(db ethdb.KeyValueWriter) { - if err := db.Delete(trieJournalKey); err != nil { - log.Crit("Failed to remove tries journal", "err", err) - } -} - // ReadStateHistoryMeta retrieves the metadata corresponding to the specified // state history. Compute the position of state history in freezer by minus // one since the id of first state history starts from one(zero for initial diff --git a/eth/backend.go b/eth/backend.go index 80ffd301ce..7616ec9d31 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -236,9 +236,13 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { VmConfig: vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, }, + // Enables file journaling for the trie database. The journal files will be stored + // within the data directory. The corresponding paths will be either: + // - DATADIR/triedb/merkle.journal + // - DATADIR/triedb/verkle.journal + TrieJournalDirectory: stack.ResolvePath("triedb"), } ) - if config.VMTrace != "" { traceConfig := json.RawMessage("{}") if config.VMTraceJsonConfig != "" { diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 23a7d383b5..e323a7449e 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "io" + "path/filepath" "sync" "time" @@ -120,6 +121,7 @@ type Config struct { StateCleanSize int // Maximum memory allowance (in bytes) for caching clean state data WriteBufferSize int // Maximum memory allowance (in bytes) for write buffer ReadOnly bool // Flag whether the database is opened in read only mode + JournalDirectory string // Absolute path of journal directory (null means the journal data is persisted in key-value store) // Testing configurations SnapshotNoBuild bool // Flag Whether the state generation is allowed @@ -156,6 +158,9 @@ func (c *Config) fields() []interface{} { } else { list = append(list, "history", fmt.Sprintf("last %d blocks", c.StateHistory)) } + if c.JournalDirectory != "" { + list = append(list, "journal-dir", c.JournalDirectory) + } return list } @@ -493,7 +498,6 @@ func (db *Database) Enable(root common.Hash) error { // Drop the stale state journal in persistent database and // reset the persistent state id back to zero. batch := db.diskdb.NewBatch() - rawdb.DeleteTrieJournal(batch) rawdb.DeleteSnapshotRoot(batch) rawdb.WritePersistentStateID(batch, 0) if err := batch.Write(); err != nil { @@ -573,8 +577,6 @@ func (db *Database) Recover(root common.Hash) error { // disk layer won't be accessible from outside. db.tree.init(dl) } - rawdb.DeleteTrieJournal(db.diskdb) - // Explicitly sync the key-value store to ensure all recent writes are // flushed to disk. This step is crucial to prevent a scenario where // recent key-value writes are lost due to an application panic, while @@ -680,6 +682,20 @@ func (db *Database) modifyAllowed() error { return nil } +// journalPath returns the absolute path of journal for persisting state data. +func (db *Database) journalPath() string { + if db.config.JournalDirectory == "" { + return "" + } + var fname string + if db.isVerkle { + fname = fmt.Sprintf("verkle.journal") + } else { + fname = fmt.Sprintf("merkle.journal") + } + return filepath.Join(db.config.JournalDirectory, fname) +} + // AccountHistory inspects the account history within the specified range. // // Start: State ID of the first history object for the query. 0 implies the first diff --git a/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go index 2982202009..e9a1850ee0 100644 --- a/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -21,12 +21,16 @@ import ( "errors" "fmt" "math/rand" + "os" + "path/filepath" + "strconv" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -121,7 +125,7 @@ type tester struct { snapStorages map[common.Hash]map[common.Hash]map[common.Hash][]byte // Keyed by the hash of account address and the hash of storage key } -func newTester(t *testing.T, historyLimit uint64, isVerkle bool, layers int, enableIndex bool) *tester { +func newTester(t *testing.T, historyLimit uint64, isVerkle bool, layers int, enableIndex bool, journalDir string) *tester { var ( disk, _ = rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{Ancient: t.TempDir()}) db = New(disk, &Config{ @@ -131,6 +135,7 @@ func newTester(t *testing.T, historyLimit uint64, isVerkle bool, layers int, ena StateCleanSize: 256 * 1024, WriteBufferSize: 256 * 1024, NoAsyncFlush: true, + JournalDirectory: journalDir, }, isVerkle) obj = &tester{ @@ -466,7 +471,7 @@ func TestDatabaseRollback(t *testing.T) { }() // Verify state histories - tester := newTester(t, 0, false, 32, false) + tester := newTester(t, 0, false, 32, false, "") defer tester.release() if err := tester.verifyHistory(); err != nil { @@ -500,7 +505,7 @@ func TestDatabaseRecoverable(t *testing.T) { }() var ( - tester = newTester(t, 0, false, 12, false) + tester = newTester(t, 0, false, 12, false, "") index = tester.bottomIndex() ) defer tester.release() @@ -544,7 +549,7 @@ func TestDisable(t *testing.T) { maxDiffLayers = 128 }() - tester := newTester(t, 0, false, 32, false) + tester := newTester(t, 0, false, 32, false, "") defer tester.release() stored := crypto.Keccak256Hash(rawdb.ReadAccountTrieNode(tester.db.diskdb, nil)) @@ -586,7 +591,7 @@ func TestCommit(t *testing.T) { maxDiffLayers = 128 }() - tester := newTester(t, 0, false, 12, false) + tester := newTester(t, 0, false, 12, false, "") defer tester.release() if err := tester.db.Commit(tester.lastHash(), false); err != nil { @@ -610,20 +615,25 @@ func TestCommit(t *testing.T) { } func TestJournal(t *testing.T) { + testJournal(t, "") + testJournal(t, filepath.Join(t.TempDir(), strconv.Itoa(rand.Intn(10000)))) +} + +func testJournal(t *testing.T, journalDir string) { // Redefine the diff layer depth allowance for faster testing. maxDiffLayers = 4 defer func() { maxDiffLayers = 128 }() - tester := newTester(t, 0, false, 12, false) + tester := newTester(t, 0, false, 12, false, journalDir) defer tester.release() if err := tester.db.Journal(tester.lastHash()); err != nil { t.Errorf("Failed to journal, err: %v", err) } tester.db.Close() - tester.db = New(tester.db.diskdb, nil, false) + tester.db = New(tester.db.diskdb, tester.db.config, false) // Verify states including disk layer and all diff on top. for i := 0; i < len(tester.roots); i++ { @@ -640,13 +650,30 @@ func TestJournal(t *testing.T) { } func TestCorruptedJournal(t *testing.T) { + testCorruptedJournal(t, "", func(db ethdb.Database) { + // Mutate the journal in disk, it should be regarded as invalid + blob := rawdb.ReadTrieJournal(db) + blob[0] = 0xa + rawdb.WriteTrieJournal(db, blob) + }) + + directory := filepath.Join(t.TempDir(), strconv.Itoa(rand.Intn(10000))) + testCorruptedJournal(t, directory, func(_ ethdb.Database) { + f, _ := os.OpenFile(filepath.Join(directory, "merkle.journal"), os.O_WRONLY, 0644) + f.WriteAt([]byte{0xa}, 0) + f.Sync() + f.Close() + }) +} + +func testCorruptedJournal(t *testing.T, journalDir string, modifyFn func(database ethdb.Database)) { // Redefine the diff layer depth allowance for faster testing. maxDiffLayers = 4 defer func() { maxDiffLayers = 128 }() - tester := newTester(t, 0, false, 12, false) + tester := newTester(t, 0, false, 12, false, journalDir) defer tester.release() if err := tester.db.Journal(tester.lastHash()); err != nil { @@ -655,13 +682,10 @@ func TestCorruptedJournal(t *testing.T) { tester.db.Close() root := crypto.Keccak256Hash(rawdb.ReadAccountTrieNode(tester.db.diskdb, nil)) - // Mutate the journal in disk, it should be regarded as invalid - blob := rawdb.ReadTrieJournal(tester.db.diskdb) - blob[0] = 0xa - rawdb.WriteTrieJournal(tester.db.diskdb, blob) + modifyFn(tester.db.diskdb) // Verify states, all not-yet-written states should be discarded - tester.db = New(tester.db.diskdb, nil, false) + tester.db = New(tester.db.diskdb, tester.db.config, false) for i := 0; i < len(tester.roots); i++ { if tester.roots[i] == root { if err := tester.verifyState(root); err != nil { @@ -694,7 +718,7 @@ func TestTailTruncateHistory(t *testing.T) { maxDiffLayers = 128 }() - tester := newTester(t, 10, false, 12, false) + tester := newTester(t, 10, false, 12, false, "") defer tester.release() tester.db.Close() diff --git a/triedb/pathdb/fileutils_unix.go b/triedb/pathdb/fileutils_unix.go new file mode 100644 index 0000000000..fde0bf50fa --- /dev/null +++ b/triedb/pathdb/fileutils_unix.go @@ -0,0 +1,57 @@ +// Copyright 2025 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 . + +//go:build !windows +// +build !windows + +package pathdb + +import ( + "errors" + "os" + "syscall" +) + +func isErrInvalid(err error) bool { + if errors.Is(err, os.ErrInvalid) { + return true + } + // Go >= 1.8 returns *os.PathError instead + if patherr, ok := err.(*os.PathError); ok && patherr.Err == syscall.EINVAL { + return true + } + return false +} + +func syncDir(name string) error { + // As per fsync manpage, Linux seems to expect fsync on directory, however + // some system don't support this, so we will ignore syscall.EINVAL. + // + // From fsync(2): + // Calling fsync() does not necessarily ensure that the entry in the + // directory containing the file has also reached disk. For that an + // explicit fsync() on a file descriptor for the directory is also needed. + f, err := os.Open(name) + if err != nil { + return err + } + defer f.Close() + + if err := f.Sync(); err != nil && !isErrInvalid(err) { + return err + } + return nil +} diff --git a/triedb/pathdb/fileutils_windows.go b/triedb/pathdb/fileutils_windows.go new file mode 100644 index 0000000000..e4c644d757 --- /dev/null +++ b/triedb/pathdb/fileutils_windows.go @@ -0,0 +1,25 @@ +// Copyright 2025 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 . + +//go:build windows +// +build windows + +package pathdb + +func syncDir(name string) error { + // On Windows, fsync on directories is not supported + return nil +} diff --git a/triedb/pathdb/history_reader_test.go b/triedb/pathdb/history_reader_test.go index 4356490f23..4eb93fb9c9 100644 --- a/triedb/pathdb/history_reader_test.go +++ b/triedb/pathdb/history_reader_test.go @@ -126,7 +126,7 @@ func testHistoryReader(t *testing.T, historyLimit uint64) { }() //log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true))) - env := newTester(t, historyLimit, false, 64, true) + env := newTester(t, historyLimit, false, 64, true, "") defer env.release() waitIndexing(env.db) diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index e88b3e062f..4639932763 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "io" + "os" "time" "github.com/ethereum/go-ethereum/common" @@ -31,6 +32,8 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +const tempJournalSuffix = ".tmp" + var ( errMissJournal = errors.New("journal not found") errMissVersion = errors.New("version not found") @@ -51,11 +54,25 @@ const journalVersion uint64 = 3 // loadJournal tries to parse the layer journal from the disk. func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) { - journal := rawdb.ReadTrieJournal(db.diskdb) - if len(journal) == 0 { - return nil, errMissJournal + var reader io.Reader + if path := db.journalPath(); path != "" && common.FileExist(path) { + // If a journal file is specified, read it from there + log.Info("Load database journal from file", "path", path) + f, err := os.OpenFile(path, os.O_RDONLY, 0644) + if err != nil { + return nil, fmt.Errorf("failed to read journal file %s: %w", path, err) + } + defer f.Close() + reader = f + } else { + log.Info("Load database journal from disk") + journal := rawdb.ReadTrieJournal(db.diskdb) + if len(journal) == 0 { + return nil, errMissJournal + } + reader = bytes.NewReader(journal) } - r := rlp.NewStream(bytes.NewReader(journal), 0) + r := rlp.NewStream(reader, 0) // Firstly, resolve the first element as the journal version version, err := r.Uint64() @@ -297,9 +314,9 @@ func (db *Database) Journal(root common.Hash) error { } disk := db.tree.bottom() if l, ok := l.(*diffLayer); ok { - log.Info("Persisting dirty state to disk", "head", l.block, "root", root, "layers", l.id-disk.id+disk.buffer.layers) + log.Info("Persisting dirty state", "head", l.block, "root", root, "layers", l.id-disk.id+disk.buffer.layers) } else { // disk layer only on noop runs (likely) or deep reorgs (unlikely) - log.Info("Persisting dirty state to disk", "root", root, "layers", disk.buffer.layers) + log.Info("Persisting dirty state", "root", root, "layers", disk.buffer.layers) } // Block until the background flushing is finished and terminate // the potential active state generator. @@ -316,8 +333,37 @@ func (db *Database) Journal(root common.Hash) error { if db.readOnly { return errDatabaseReadOnly } + + // Store the journal into the database and return + var ( + file *os.File + journal io.Writer + journalPath = db.journalPath() + ) + if journalPath != "" { + // Write into a temp file first + err := os.MkdirAll(db.config.JournalDirectory, 0755) + if err != nil { + return err + } + tmp := journalPath + tempJournalSuffix + file, err = os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return fmt.Errorf("failed to open journal file %s: %w", tmp, err) + } + defer func() { + if file != nil { + file.Close() + os.Remove(tmp) // Clean up temp file if we didn't successfully rename it + log.Warn("Removed leftover temporary journal file", "path", tmp) + } + }() + journal = file + } else { + journal = new(bytes.Buffer) + } + // Firstly write out the metadata of journal - journal := new(bytes.Buffer) if err := rlp.Encode(journal, journalVersion); err != nil { return err } @@ -334,11 +380,38 @@ func (db *Database) Journal(root common.Hash) error { if err := l.journal(journal); err != nil { return err } - // Store the journal into the database and return - rawdb.WriteTrieJournal(db.diskdb, journal.Bytes()) + // Store the journal into the database and return + if file == nil { + data := journal.(*bytes.Buffer) + size := data.Len() + rawdb.WriteTrieJournal(db.diskdb, data.Bytes()) + log.Info("Persisted dirty state to disk", "size", common.StorageSize(size), "elapsed", common.PrettyDuration(time.Since(start))) + } else { + stat, err := file.Stat() + if err != nil { + return err + } + size := int(stat.Size()) + + // Close the temporary file and atomically rename it + if err := file.Sync(); err != nil { + return fmt.Errorf("failed to fsync the journal, %v", err) + } + if err := file.Close(); err != nil { + return fmt.Errorf("failed to close the journal: %v", err) + } + // Replace the live journal with the newly generated one + if err := os.Rename(journalPath+tempJournalSuffix, journalPath); err != nil { + return fmt.Errorf("failed to rename the journal: %v", err) + } + if err := syncDir(db.config.JournalDirectory); err != nil { + return fmt.Errorf("failed to fsync the dir: %v", err) + } + file = nil + log.Info("Persisted dirty state to file", "path", journalPath, "size", common.StorageSize(size), "elapsed", common.PrettyDuration(time.Since(start))) + } // Set the db in read only mode to reject all following mutations db.readOnly = true - log.Info("Persisted dirty state to disk", "size", common.StorageSize(journal.Len()), "elapsed", common.PrettyDuration(time.Since(start))) return nil } From 7364e63ef90f0dc4589d96a38e450dcfe7183caa Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 15 Jul 2025 13:50:52 +0800 Subject: [PATCH 14/56] core/rawdb: change the mechanism to schedule freezer sync (#32135) This pull request slightly improves the freezer fsync mechanism by scheduling the Sync operation based on the number of uncommitted items and original time interval. Originally, freezer.Sync was triggered every 30 seconds, which worked well during active chain synchronization. However, once the initial state sync is complete, the fixed interval causes Sync to be scheduled too frequently. To address this, the scheduling logic has been improved to consider both the time interval and the number of uncommitted items. This additional condition helps avoid unnecessary Sync operations when the chain is idle. --- core/rawdb/freezer_batch.go | 18 ++++++++++++++---- core/rawdb/freezer_table.go | 5 +++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/core/rawdb/freezer_batch.go b/core/rawdb/freezer_batch.go index 99b63df4dc..7e46e49f43 100644 --- a/core/rawdb/freezer_batch.go +++ b/core/rawdb/freezer_batch.go @@ -25,9 +25,16 @@ import ( "github.com/golang/snappy" ) -// This is the maximum amount of data that will be buffered in memory -// for a single freezer table batch. -const freezerBatchBufferLimit = 2 * 1024 * 1024 +const ( + // This is the maximum amount of data that will be buffered in memory + // for a single freezer table batch. + freezerBatchBufferLimit = 2 * 1024 * 1024 + + // freezerTableFlushThreshold defines the threshold for triggering a freezer + // table sync operation. If the number of accumulated uncommitted items exceeds + // this value, a sync will be scheduled. + freezerTableFlushThreshold = 512 +) // freezerBatch is a write operation of multiple items on a freezer. type freezerBatch struct { @@ -201,6 +208,7 @@ func (batch *freezerTableBatch) commit() error { // Update headBytes of table. batch.t.headBytes += dataSize + items := batch.curItem - batch.t.items.Load() batch.t.items.Store(batch.curItem) // Update metrics. @@ -208,7 +216,9 @@ func (batch *freezerTableBatch) commit() error { batch.t.writeMeter.Mark(dataSize + indexSize) // Periodically sync the table, todo (rjl493456442) make it configurable? - if time.Since(batch.t.lastSync) > 30*time.Second { + batch.t.uncommitted += items + if batch.t.uncommitted > freezerTableFlushThreshold && time.Since(batch.t.lastSync) > 30*time.Second { + batch.t.uncommitted = 0 batch.t.lastSync = time.Now() return batch.t.Sync() } diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go index 3900b5d558..19c40cc16e 100644 --- a/core/rawdb/freezer_table.go +++ b/core/rawdb/freezer_table.go @@ -112,8 +112,9 @@ type freezerTable struct { headId uint32 // number of the currently active head file tailId uint32 // number of the earliest file - metadata *freezerTableMeta // metadata of the table - lastSync time.Time // Timestamp when the last sync was performed + metadata *freezerTableMeta // metadata of the table + uncommitted uint64 // Count of items written without flushing to file + lastSync time.Time // Timestamp when the last sync was performed headBytes int64 // Number of bytes written to the head file readMeter *metrics.Meter // Meter for measuring the effective amount of data read From d7db10ddbde2b1a80485e25808410a2a3b44b836 Mon Sep 17 00:00:00 2001 From: asamuj <105436033+asamuj@users.noreply.github.com> Date: Tue, 15 Jul 2025 14:20:45 +0800 Subject: [PATCH 15/56] eth/protocols/snap, p2p/discover: improve zero time checks (#32214) --- eth/protocols/snap/sync.go | 6 ++---- p2p/discover/table.go | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go index 9e079f540f..9982d0f55c 100644 --- a/eth/protocols/snap/sync.go +++ b/eth/protocols/snap/sync.go @@ -615,7 +615,7 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error { s.statelessPeers = make(map[string]struct{}) s.lock.Unlock() - if s.startTime == (time.Time{}) { + if s.startTime.IsZero() { s.startTime = time.Now() } // Retrieve the previous sync status from LevelDB and abort if already synced @@ -1987,9 +1987,7 @@ func (s *Syncer) processAccountResponse(res *accountResponse) { func (s *Syncer) processBytecodeResponse(res *bytecodeResponse) { batch := s.db.NewBatch() - var ( - codes uint64 - ) + var codes uint64 for i, hash := range res.hashes { code := res.codes[i] diff --git a/p2p/discover/table.go b/p2p/discover/table.go index 6a3d1af3af..b6c35aaaa9 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -562,7 +562,7 @@ func (tab *Table) addReplacement(b *bucket, n *enode.Node) { } func (tab *Table) nodeAdded(b *bucket, n *tableNode) { - if n.addedToTable == (time.Time{}) { + if n.addedToTable.IsZero() { n.addedToTable = time.Now() } n.addedToBucket = time.Now() From 7fcb796f646fb0580e78c1cd5540a591adc75d52 Mon Sep 17 00:00:00 2001 From: Maxim Evtush <154841002+maximevtush@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:24:17 +0300 Subject: [PATCH 16/56] all: update dead wiki links (#32215) --- **Description:** - Replaced outdated GitHub wiki links with current, official documentation URLs. - Removed links that redirect or are no longer relevant. - Ensured all references point to up-to-date and reliable sources. --- --- rpc/doc.go | 2 +- tests/block_test_util.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc/doc.go b/rpc/doc.go index 7c87793dca..4bc0d6d8f7 100644 --- a/rpc/doc.go +++ b/rpc/doc.go @@ -98,7 +98,7 @@ Subscriptions are deleted when the user sends an unsubscribe request or when the connection which was used to create the subscription is closed. This can be initiated by the client and server. The server will close the connection for any write error. -For more information about subscriptions, see https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB. +For more information about subscriptions, see https://geth.ethereum.org/docs/interacting-with-geth/rpc/pubsub # Reverse Calls diff --git a/tests/block_test_util.go b/tests/block_test_util.go index a76037405b..3b88753b1c 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -222,7 +222,7 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis { } /* -See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II +See https://ethereum-tests.readthedocs.io/en/latest/blockchain-ref.html Whether a block is valid or not is a bit subtle, it's defined by presence of blockHeader, transactions and uncleHeaders fields. If they are missing, the block is From e94123acc2bc8283764b26f3423f5e026515c0f4 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 15 Jul 2025 15:48:36 +0200 Subject: [PATCH 17/56] core/rawdb: reduce allocations in rawdb.ReadHeaderNumber (#31913) This is something interesting I came across during my benchmarks, we spent ~3.8% of all allocations allocating the header number on the heap. ``` (pprof) list GetHeaderByHash Total: 38197204475 ROUTINE ======================== github.com/ethereum/go-ethereum/core.(*BlockChain).GetHeaderByHash in github.com/ethereum/go-ethereum/core/blockchain_reader.go 0 5786566117 (flat, cum) 15.15% of Total . . 79:func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header { . 5786566117 80: return bc.hc.GetHeaderByHash(hash) . . 81:} . . 82: . . 83:// GetHeaderByNumber retrieves a block header from the database by number, . . 84:// caching it (associated with its hash) if found. . . 85:func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header { ROUTINE ======================== github.com/ethereum/go-ethereum/core.(*HeaderChain).GetHeaderByHash in github.com/ethereum/go-ethereum/core/headerchain.go 0 5786566117 (flat, cum) 15.15% of Total . . 404:func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header { . 1471264309 405: number := hc.GetBlockNumber(hash) . . 406: if number == nil { . . 407: return nil . . 408: } . 4315301808 409: return hc.GetHeader(hash, *number) . . 410:} . . 411: . . 412:// HasHeader checks if a block header is present in the database or not. . . 413:// In theory, if header is present in the database, all relative components . . 414:// like td and hash->number should be present too. (pprof) list GetBlockNumber Total: 38197204475 ROUTINE ======================== github.com/ethereum/go-ethereum/core.(*HeaderChain).GetBlockNumber in github.com/ethereum/go-ethereum/core/headerchain.go 94438817 1471264309 (flat, cum) 3.85% of Total . . 100:func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 { 94438817 94438817 101: if cached, ok := hc.numberCache.Get(hash); ok { . . 102: return &cached . . 103: } . 1376270828 104: number := rawdb.ReadHeaderNumber(hc.chainDb, hash) . . 105: if number != nil { . 554664 106: hc.numberCache.Add(hash, *number) . . 107: } . . 108: return number . . 109:} . . 110: . . 111:type headerWriteResult struct { (pprof) list ReadHeaderNumber Total: 38197204475 ROUTINE ======================== github.com/ethereum/go-ethereum/core/rawdb.ReadHeaderNumber in github.com/ethereum/go-ethereum/core/rawdb/accessors_chain.go 204606513 1376270828 (flat, cum) 3.60% of Total . . 146:func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 { 109577863 1281242178 147: data, _ := db.Get(headerNumberKey(hash)) . . 148: if len(data) != 8 { . . 149: return nil . . 150: } 95028650 95028650 151: number := binary.BigEndian.Uint64(data) . . 152: return &number . . 153:} . . 154: . . 155:// WriteHeaderNumber stores the hash->number mapping. . . 156:func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { ``` Opening this to discuss the idea, I know that rawdb.EmptyNumber is not a great name for the variable, open to suggestions --- cmd/geth/chaincmd.go | 4 +-- core/blockchain_reader.go | 45 ++++++++++++++++--------------- core/blockchain_test.go | 6 ++--- core/headerchain.go | 18 ++++++------- core/rawdb/accessors_chain.go | 18 ++++++------- core/rawdb/accessors_indexes.go | 6 ++++- core/rawdb/chain_freezer.go | 12 ++++----- core/rawdb/database.go | 7 ++++- core/txindexer.go | 6 ++--- eth/filters/filter_system_test.go | 24 ++++++++--------- 10 files changed, 79 insertions(+), 67 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index acd7b5c230..44e11dbf06 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -576,8 +576,8 @@ func parseDumpConfig(ctx *cli.Context, db ethdb.Database) (*state.DumpConfig, co arg := ctx.Args().First() if hashish(arg) { hash := common.HexToHash(arg) - if number := rawdb.ReadHeaderNumber(db, hash); number != nil { - header = rawdb.ReadHeader(db, hash, *number) + if number, ok := rawdb.ReadHeaderNumber(db, hash); ok { + header = rawdb.ReadHeader(db, hash, number) } else { return nil, common.Hash{}, fmt.Errorf("block %x not found", hash) } diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 7626e9e30d..4894523b0e 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -91,7 +91,10 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header { // GetBlockNumber retrieves the block number associated with a block hash. func (bc *BlockChain) GetBlockNumber(hash common.Hash) *uint64 { - return bc.hc.GetBlockNumber(hash) + if num, ok := bc.hc.GetBlockNumber(hash); ok { + return &num + } + return nil } // GetHeadersFrom returns a contiguous segment of headers, in rlp-form, going @@ -107,11 +110,11 @@ func (bc *BlockChain) GetBody(hash common.Hash) *types.Body { if cached, ok := bc.bodyCache.Get(hash); ok { return cached } - number := bc.hc.GetBlockNumber(hash) - if number == nil { + number, ok := bc.hc.GetBlockNumber(hash) + if !ok { return nil } - body := rawdb.ReadBody(bc.db, hash, *number) + body := rawdb.ReadBody(bc.db, hash, number) if body == nil { return nil } @@ -127,11 +130,11 @@ func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue { if cached, ok := bc.bodyRLPCache.Get(hash); ok { return cached } - number := bc.hc.GetBlockNumber(hash) - if number == nil { + number, ok := bc.hc.GetBlockNumber(hash) + if !ok { return nil } - body := rawdb.ReadBodyRLP(bc.db, hash, *number) + body := rawdb.ReadBodyRLP(bc.db, hash, number) if len(body) == 0 { return nil } @@ -180,11 +183,11 @@ func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { // GetBlockByHash retrieves a block from the database by hash, caching it if found. func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block { - number := bc.hc.GetBlockNumber(hash) - if number == nil { + number, ok := bc.hc.GetBlockNumber(hash) + if !ok { return nil } - return bc.GetBlock(hash, *number) + return bc.GetBlock(hash, number) } // GetBlockByNumber retrieves a block from the database by number, caching it @@ -200,18 +203,18 @@ func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block { // GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. // [deprecated by eth/62] func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { - number := bc.hc.GetBlockNumber(hash) - if number == nil { + number, ok := bc.hc.GetBlockNumber(hash) + if !ok { return nil } for i := 0; i < n; i++ { - block := bc.GetBlock(hash, *number) + block := bc.GetBlock(hash, number) if block == nil { break } blocks = append(blocks, block) hash = block.ParentHash() - *number-- + number-- } return } @@ -259,15 +262,15 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts { if receipts, ok := bc.receiptsCache.Get(hash); ok { return receipts } - number := rawdb.ReadHeaderNumber(bc.db, hash) - if number == nil { + number, ok := rawdb.ReadHeaderNumber(bc.db, hash) + if !ok { return nil } - header := bc.GetHeader(hash, *number) + header := bc.GetHeader(hash, number) if header == nil { return nil } - receipts := rawdb.ReadReceipts(bc.db, hash, *number, header.Time, bc.chainConfig) + receipts := rawdb.ReadReceipts(bc.db, hash, number, header.Time, bc.chainConfig) if receipts == nil { return nil } @@ -286,11 +289,11 @@ func (bc *BlockChain) GetRawReceipts(hash common.Hash, number uint64) types.Rece // GetReceiptsRLP retrieves the receipts of a block. func (bc *BlockChain) GetReceiptsRLP(hash common.Hash) rlp.RawValue { - number := rawdb.ReadHeaderNumber(bc.db, hash) - if number == nil { + number, ok := rawdb.ReadHeaderNumber(bc.db, hash) + if !ok { return nil } - return rawdb.ReadReceiptsRLP(bc.db, hash, *number) + return rawdb.ReadReceiptsRLP(bc.db, hash, number) } // GetUnclesInChain retrieves all the uncles from a given block backwards until diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 5e768fccdf..b749798f9c 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -782,13 +782,13 @@ func testFastVsFullChains(t *testing.T, scheme string) { } // Check that hash-to-number mappings are present in all databases. - if m := rawdb.ReadHeaderNumber(fastDb, hash); m == nil || *m != num { + if m, ok := rawdb.ReadHeaderNumber(fastDb, hash); !ok || m != num { t.Errorf("block #%d [%x]: wrong hash-to-number mapping in fastdb: %v", num, hash, m) } - if m := rawdb.ReadHeaderNumber(ancientDb, hash); m == nil || *m != num { + if m, ok := rawdb.ReadHeaderNumber(ancientDb, hash); !ok || m != num { t.Errorf("block #%d [%x]: wrong hash-to-number mapping in ancientdb: %v", num, hash, m) } - if m := rawdb.ReadHeaderNumber(archiveDb, hash); m == nil || *m != num { + if m, ok := rawdb.ReadHeaderNumber(archiveDb, hash); !ok || m != num { t.Errorf("block #%d [%x]: wrong hash-to-number mapping in archivedb: %v", num, hash, m) } } diff --git a/core/headerchain.go b/core/headerchain.go index 6e70dfa865..a33222e9fd 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -97,15 +97,15 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c // GetBlockNumber retrieves the block number belonging to the given hash // from the cache or database -func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 { +func (hc *HeaderChain) GetBlockNumber(hash common.Hash) (uint64, bool) { if cached, ok := hc.numberCache.Get(hash); ok { - return &cached + return cached, true } - number := rawdb.ReadHeaderNumber(hc.chainDb, hash) - if number != nil { - hc.numberCache.Add(hash, *number) + number, ok := rawdb.ReadHeaderNumber(hc.chainDb, hash) + if ok { + hc.numberCache.Add(hash, number) } - return number + return number, ok } type headerWriteResult struct { @@ -402,11 +402,11 @@ func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header // GetHeaderByHash retrieves a block header from the database by hash, caching it if // found. func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header { - number := hc.GetBlockNumber(hash) - if number == nil { + number, ok := hc.GetBlockNumber(hash) + if !ok { return nil } - return hc.GetHeader(hash, *number) + return hc.GetHeader(hash, number) } // HasHeader checks if a block header is present in the database or not. diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 4426c6a9e7..0782a0e7da 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -143,13 +143,13 @@ func ReadAllCanonicalHashes(db ethdb.Iteratee, from uint64, to uint64, limit int } // ReadHeaderNumber returns the header number assigned to a hash. -func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 { +func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) (uint64, bool) { data, _ := db.Get(headerNumberKey(hash)) if len(data) != 8 { - return nil + return 0, false } number := binary.BigEndian.Uint64(data) - return &number + return number, true } // WriteHeaderNumber stores the hash->number mapping. @@ -935,11 +935,11 @@ func ReadHeadHeader(db ethdb.Reader) *types.Header { if headHeaderHash == (common.Hash{}) { return nil } - headHeaderNumber := ReadHeaderNumber(db, headHeaderHash) - if headHeaderNumber == nil { + headHeaderNumber, ok := ReadHeaderNumber(db, headHeaderHash) + if !ok { return nil } - return ReadHeader(db, headHeaderHash, *headHeaderNumber) + return ReadHeader(db, headHeaderHash, headHeaderNumber) } // ReadHeadBlock returns the current canonical head block. @@ -948,9 +948,9 @@ func ReadHeadBlock(db ethdb.Reader) *types.Block { if headBlockHash == (common.Hash{}) { return nil } - headBlockNumber := ReadHeaderNumber(db, headBlockHash) - if headBlockNumber == nil { + headBlockNumber, ok := ReadHeaderNumber(db, headBlockHash) + if !ok { return nil } - return ReadBlock(db, headBlockHash, *headBlockNumber) + return ReadBlock(db, headBlockHash, headBlockNumber) } diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go index f71bbb500b..a725f144d4 100644 --- a/core/rawdb/accessors_indexes.go +++ b/core/rawdb/accessors_indexes.go @@ -41,7 +41,11 @@ func DecodeTxLookupEntry(data []byte, db ethdb.Reader) *uint64 { } // Database v4-v5 tx lookup format just stores the hash if len(data) == common.HashLength { - return ReadHeaderNumber(db, common.BytesToHash(data)) + number, ok := ReadHeaderNumber(db, common.BytesToHash(data)) + if !ok { + return nil + } + return &number } // Finally try database v3 tx lookup format var entry LegacyTxLookupEntry diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go index 3a5218c023..4834354b22 100644 --- a/core/rawdb/chain_freezer.go +++ b/core/rawdb/chain_freezer.go @@ -107,12 +107,12 @@ func (f *chainFreezer) readHeadNumber(db ethdb.KeyValueReader) uint64 { log.Error("Head block is not reachable") return 0 } - number := ReadHeaderNumber(db, hash) - if number == nil { + number, ok := ReadHeaderNumber(db, hash) + if !ok { log.Error("Number of head block is missing") return 0 } - return *number + return number } // readFinalizedNumber returns the number of finalized block. 0 is returned @@ -122,12 +122,12 @@ func (f *chainFreezer) readFinalizedNumber(db ethdb.KeyValueReader) uint64 { if hash == (common.Hash{}) { return 0 } - number := ReadHeaderNumber(db, hash) - if number == nil { + number, ok := ReadHeaderNumber(db, hash) + if !ok { log.Error("Number of finalized block is missing") return 0 } - return *number + return number } // freezeThreshold returns the threshold for chain freezing. It's determined diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 9681c39c58..2ebdf360b5 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -268,7 +268,12 @@ func Open(db ethdb.KeyValueStore, opts OpenOptions) (ethdb.Database, error) { if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 { // Subsequent header after the freezer limit is missing from the database. // Reject startup if the database has a more recent head. - if head := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); head > frozen-1 { + head, ok := ReadHeaderNumber(db, ReadHeadHeaderHash(db)) + if !ok { + printChainMetadata(db) + return nil, fmt.Errorf("could not read header number, hash %v", ReadHeadHeaderHash(db)) + } + if head > frozen-1 { // Find the smallest block stored in the key-value store // in range of [frozen, head] var number uint64 diff --git a/core/txindexer.go b/core/txindexer.go index 587118ed7f..b2a94a6ead 100644 --- a/core/txindexer.go +++ b/core/txindexer.go @@ -217,11 +217,11 @@ func (indexer *txIndexer) resolveHead() uint64 { if headBlockHash == (common.Hash{}) { return 0 } - headBlockNumber := rawdb.ReadHeaderNumber(indexer.db, headBlockHash) - if headBlockNumber == nil { + headBlockNumber, ok := rawdb.ReadHeaderNumber(indexer.db, headBlockHash) + if !ok { return 0 } - return *headBlockNumber + return headBlockNumber } // loop is the scheduler of the indexer, assigning indexing/unindexing tasks depending diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 10f82364b5..3e686ca2eb 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -92,18 +92,18 @@ func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumbe switch blockNr { case rpc.LatestBlockNumber: hash = rawdb.ReadHeadBlockHash(b.db) - number := rawdb.ReadHeaderNumber(b.db, hash) - if number == nil { + number, ok := rawdb.ReadHeaderNumber(b.db, hash) + if !ok { return nil, nil } - num = *number + num = number case rpc.FinalizedBlockNumber: hash = rawdb.ReadFinalizedBlockHash(b.db) - number := rawdb.ReadHeaderNumber(b.db, hash) - if number == nil { + number, ok := rawdb.ReadHeaderNumber(b.db, hash) + if !ok { return nil, nil } - num = *number + num = number case rpc.SafeBlockNumber: return nil, errors.New("safe block not found") default: @@ -114,11 +114,11 @@ func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumbe } func (b *testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { - number := rawdb.ReadHeaderNumber(b.db, hash) - if number == nil { + number, ok := rawdb.ReadHeaderNumber(b.db, hash) + if !ok { return nil, nil } - return rawdb.ReadHeader(b.db, hash, *number), nil + return rawdb.ReadHeader(b.db, hash, number), nil } func (b *testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) { @@ -129,9 +129,9 @@ func (b *testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc. } func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { - if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil { - if header := rawdb.ReadHeader(b.db, hash, *number); header != nil { - return rawdb.ReadReceipts(b.db, hash, *number, header.Time, params.TestChainConfig), nil + if number, ok := rawdb.ReadHeaderNumber(b.db, hash); ok { + if header := rawdb.ReadHeader(b.db, hash, number); header != nil { + return rawdb.ReadReceipts(b.db, hash, number, header.Time, params.TestChainConfig), nil } } return nil, nil From 61d7279e1f13887b30335c89abdc8a947f0ae814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Irmak?= Date: Wed, 16 Jul 2025 16:00:39 +0300 Subject: [PATCH 18/56] trie: avoid spawning goroutines for empty children (#32220) --- trie/hasher.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/trie/hasher.go b/trie/hasher.go index 393cb0bd4d..16606808c9 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -105,18 +105,18 @@ func (h *hasher) hashFullNodeChildren(n *fullNode) *fullNode { var children [17]node if h.parallel { var wg sync.WaitGroup - wg.Add(16) for i := 0; i < 16; i++ { - go func(i int) { - hasher := newHasher(false) - if child := n.Children[i]; child != nil { + if child := n.Children[i]; child != nil { + wg.Add(1) + go func(i int) { + hasher := newHasher(false) children[i] = hasher.hash(child, false) - } else { - children[i] = nilValueNode - } - returnHasherToPool(hasher) - wg.Done() - }(i) + returnHasherToPool(hasher) + wg.Done() + }(i) + } else { + children[i] = nilValueNode + } } wg.Wait() } else { From 532a1c2ca499143bd56efb48322a3a41b5c51695 Mon Sep 17 00:00:00 2001 From: CertiK-Geth <138698582+CertiK-Geth@users.noreply.github.com> Date: Wed, 16 Jul 2025 21:11:10 +0800 Subject: [PATCH 19/56] eth/downloader: improve nil pointer protection (#32222) Fix #32221 --------- Co-authored-by: rjl493456442 --- eth/downloader/beaconsync.go | 3 +++ eth/downloader/skeleton.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/eth/downloader/beaconsync.go b/eth/downloader/beaconsync.go index 33ad0f8971..12b74a1ba9 100644 --- a/eth/downloader/beaconsync.go +++ b/eth/downloader/beaconsync.go @@ -250,6 +250,9 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) { check := (start + end) / 2 h := d.skeleton.Header(check) + if h == nil { + return 0, fmt.Errorf("filled skeleton header is missing: %d", check) + } n := h.Number.Uint64() var known bool diff --git a/eth/downloader/skeleton.go b/eth/downloader/skeleton.go index 206d28414e..2cf9c4672b 100644 --- a/eth/downloader/skeleton.go +++ b/eth/downloader/skeleton.go @@ -1150,6 +1150,9 @@ func (s *skeleton) cleanStales(filled *types.Header) error { if number < s.progress.Subchains[0].Head { // The skeleton chain is partially consumed, set the new tail as filled+1. tail := rawdb.ReadSkeletonHeader(s.db, number+1) + if tail == nil { + return fmt.Errorf("filled header is missing: %d", number+1) + } if tail.ParentHash != filled.Hash() { return fmt.Errorf("filled header is discontinuous with subchain: %d %s, please file an issue", number, filled.Hash()) } From 66df1f26b86ab2c433c802c25e2a0534871511fb Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 16 Jul 2025 21:36:44 +0800 Subject: [PATCH 20/56] account/abi/bind/v2: fix TestDeploymentWithOverrides (#32212) The root cause of the flaky test was a nonce conflict caused by async contract deployments. This solution defines a custom deployer with automatic nonce management. --- accounts/abi/bind/v2/lib.go | 25 +++++++++++++++++++++++++ accounts/abi/bind/v2/lib_test.go | 12 ++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/accounts/abi/bind/v2/lib.go b/accounts/abi/bind/v2/lib.go index 3831161341..f2a49d6799 100644 --- a/accounts/abi/bind/v2/lib.go +++ b/accounts/abi/bind/v2/lib.go @@ -28,6 +28,7 @@ package bind import ( "errors" + "math/big" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" @@ -241,3 +242,27 @@ func DefaultDeployer(opts *TransactOpts, backend ContractBackend) DeployFn { return addr, tx, nil } } + +// DeployerWithNonceAssignment is basically identical to DefaultDeployer, +// but it additionally tracks the nonce to enable automatic assignment. +// +// This is especially useful when deploying multiple contracts +// from the same address — whether they are independent contracts +// or part of a dependency chain that must be deployed in order. +func DeployerWithNonceAssignment(opts *TransactOpts, backend ContractBackend) DeployFn { + var pendingNonce int64 + if opts.Nonce != nil { + pendingNonce = opts.Nonce.Int64() + } + return func(input []byte, deployer []byte) (common.Address, *types.Transaction, error) { + if pendingNonce != 0 { + opts.Nonce = big.NewInt(pendingNonce) + } + addr, tx, err := DeployContract(opts, deployer, backend, input) + if err != nil { + return common.Address{}, nil, err + } + pendingNonce = int64(tx.Nonce() + 1) + return addr, tx, nil + } +} diff --git a/accounts/abi/bind/v2/lib_test.go b/accounts/abi/bind/v2/lib_test.go index fc895edad5..ee1db9cf86 100644 --- a/accounts/abi/bind/v2/lib_test.go +++ b/accounts/abi/bind/v2/lib_test.go @@ -65,6 +65,14 @@ func makeTestDeployer(backend simulated.Client) func(input, deployer []byte) (co return bind.DefaultDeployer(bind.NewKeyedTransactor(testKey, chainId), backend) } +// makeTestDeployerWithNonceAssignment is similar to makeTestDeployer, +// but it returns a deployer that automatically tracks nonce, +// enabling the deployment of multiple contracts from the same account. +func makeTestDeployerWithNonceAssignment(backend simulated.Client) func(input, deployer []byte) (common.Address, *types.Transaction, error) { + chainId, _ := backend.ChainID(context.Background()) + return bind.DeployerWithNonceAssignment(bind.NewKeyedTransactor(testKey, chainId), backend) +} + // test that deploying a contract with library dependencies works, // verifying by calling method on the deployed contract. func TestDeploymentLibraries(t *testing.T) { @@ -80,7 +88,7 @@ func TestDeploymentLibraries(t *testing.T) { Contracts: []*bind.MetaData{&nested_libraries.C1MetaData}, Inputs: map[string][]byte{nested_libraries.C1MetaData.ID: constructorInput}, } - res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend.Client)) + res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployerWithNonceAssignment(bindBackend.Client)) if err != nil { t.Fatalf("err: %+v\n", err) } @@ -122,7 +130,7 @@ func TestDeploymentWithOverrides(t *testing.T) { deploymentParams := &bind.DeploymentParams{ Contracts: nested_libraries.C1MetaData.Deps, } - res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend)) + res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployerWithNonceAssignment(bindBackend)) if err != nil { t.Fatalf("err: %+v\n", err) } From 30e3a4918031775b64cf08d7b2d70345554b7d1e Mon Sep 17 00:00:00 2001 From: shazam8253 <54690736+shazam8253@users.noreply.github.com> Date: Wed, 16 Jul 2025 23:26:33 +0200 Subject: [PATCH 21/56] eth/tracers: apply block header overrides correctly (#32183) Fixes #32175. This fixes the scenario where the blockhash opcode would return 0x0 during RPC simulations when using BlockOverrides with a future block number. The root cause was that BlockOverrides.Apply() only modified the vm.BlockContext, but GetHashFn() depends on the actual types.Header.Number to resolve valid historical block hashes. This caused a mismatch and resulted in incorrect behavior during trace and call simulations. --------- Co-authored-by: shantichanal <158101918+shantichanal@users.noreply.github.com> Co-authored-by: lightclient --- eth/tracers/api.go | 35 ++++++++++++++++++++++++----------- eth/tracers/api_test.go | 20 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 6962023343..b50a532b60 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -953,40 +953,53 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } defer release() - vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + h := block.Header() + blockContext := core.NewEVMBlockContext(h, api.chainContext(ctx), nil) + // Apply the customization rules if required. if config != nil { - if overrideErr := config.BlockOverrides.Apply(&vmctx); overrideErr != nil { - return nil, overrideErr + if config.BlockOverrides != nil && config.BlockOverrides.Number.ToInt().Uint64() == h.Number.Uint64()+1 { + // Overriding the block number to n+1 is a common way for wallets to + // simulate transactions, however without the following fix, a contract + // can assert it is being simulated by checking if blockhash(n) == 0x0 and + // can behave differently during the simulation. (#32175 for more info) + // -- + // Modify the parent hash and number so that downstream, blockContext's + // GetHash function can correctly return n. + h.ParentHash = h.Hash() + h.Number.Add(h.Number, big.NewInt(1)) } - rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time) - + if err := config.BlockOverrides.Apply(&blockContext); err != nil { + return nil, err + } + rules := api.backend.ChainConfig().Rules(blockContext.BlockNumber, blockContext.Random != nil, blockContext.Time) precompiles = vm.ActivePrecompiledContracts(rules) if err := config.StateOverrides.Apply(statedb, precompiles); err != nil { return nil, err } } - // Execute the trace - if err := args.CallDefaults(api.backend.RPCGasCap(), vmctx.BaseFee, api.backend.ChainConfig().ChainID); err != nil { + + // Execute the trace. + if err := args.CallDefaults(api.backend.RPCGasCap(), blockContext.BaseFee, api.backend.ChainConfig().ChainID); err != nil { return nil, err } var ( - msg = args.ToMessage(vmctx.BaseFee, true, true) + msg = args.ToMessage(blockContext.BaseFee, true, true) tx = args.ToTransaction(types.LegacyTxType) traceConfig *TraceConfig ) // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). if msg.GasPrice.Sign() == 0 { - vmctx.BaseFee = new(big.Int) + blockContext.BaseFee = new(big.Int) } if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { - vmctx.BlobBaseFee = new(big.Int) + blockContext.BlobBaseFee = new(big.Int) } if config != nil { traceConfig = &config.TraceConfig } - return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig, precompiles) + return api.traceTx(ctx, tx, msg, new(Context), blockContext, statedb, traceConfig, precompiles) } // traceTx configures a new tracer according to the provided configuration, and diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 076e6fd8d4..7ed2a5936e 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -689,6 +689,7 @@ func TestTracingWithOverrides(t *testing.T) { Failed bool ReturnValue string } + var testSuite = []struct { blockNumber rpc.BlockNumber call ethapi.TransactionArgs @@ -788,6 +789,25 @@ func TestTracingWithOverrides(t *testing.T) { }, want: `{"gas":72666,"failed":false,"returnValue":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`, }, + { // Override blocknumber with block n+1 and query a blockhash (resolves issue #32175) + blockNumber: rpc.LatestBlockNumber, + call: ethapi.TransactionArgs{ + From: &accounts[0].addr, + Input: newRPCBytes([]byte{ + byte(vm.PUSH1), byte(genBlocks), + byte(vm.BLOCKHASH), + byte(vm.PUSH1), 0x00, + byte(vm.MSTORE), + byte(vm.PUSH1), 0x20, + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), + }), + }, + config: &TraceCallConfig{ + BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(int64(genBlocks + 1)))}, + }, + want: fmt.Sprintf(`{"gas":59590,"failed":false,"returnValue":"%s"}`, backend.chain.GetHeaderByNumber(uint64(genBlocks)).Hash().Hex()), + }, /* pragma solidity =0.8.12; From f36d349918926e53f62844e44bcb3257d97c11b7 Mon Sep 17 00:00:00 2001 From: Delweng Date: Thu, 17 Jul 2025 10:44:35 +0800 Subject: [PATCH 22/56] triedb/pathdb: avoid duplicate metadata reads (#32226) --- triedb/pathdb/history_reader.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/triedb/pathdb/history_reader.go b/triedb/pathdb/history_reader.go index 9471ab423d..8f72d4053e 100644 --- a/triedb/pathdb/history_reader.go +++ b/triedb/pathdb/history_reader.go @@ -122,19 +122,14 @@ type indexReaderWithLimitTag struct { } // newIndexReaderWithLimitTag constructs a index reader with indexing position. -func newIndexReaderWithLimitTag(db ethdb.KeyValueReader, state stateIdent) (*indexReaderWithLimitTag, error) { - // Read the last indexed ID before the index reader construction - metadata := loadIndexMetadata(db) - if metadata == nil { - return nil, errors.New("state history hasn't been indexed yet") - } +func newIndexReaderWithLimitTag(db ethdb.KeyValueReader, state stateIdent, limit uint64) (*indexReaderWithLimitTag, error) { r, err := newIndexReader(db, state) if err != nil { return nil, err } return &indexReaderWithLimitTag{ reader: r, - limit: metadata.Last, + limit: limit, db: db, }, nil } @@ -348,7 +343,7 @@ func (r *historyReader) read(state stateIdentQuery, stateID uint64, lastID uint6 // state retrieval ir, ok := r.readers[state.String()] if !ok { - ir, err = newIndexReaderWithLimitTag(r.disk, state.stateIdent) + ir, err = newIndexReaderWithLimitTag(r.disk, state.stateIdent, metadata.Last) if err != nil { return nil, err } From becca46010178249d79af7ef5c5ea0837f2f78ec Mon Sep 17 00:00:00 2001 From: Zhou Date: Wed, 16 Jul 2025 23:59:47 -0300 Subject: [PATCH 23/56] eth/protocols/snap: fix negative eta in state progress logging (#32225) --- eth/protocols/snap/sync.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go index 9982d0f55c..84ceb9105e 100644 --- a/eth/protocols/snap/sync.go +++ b/eth/protocols/snap/sync.go @@ -3111,6 +3111,10 @@ func (s *Syncer) reportSyncProgress(force bool) { if estBytes < 1.0 { return } + // Cap the estimated state size using the synced size to avoid negative values + if estBytes < float64(synced) { + estBytes = float64(synced) + } elapsed := time.Since(s.startTime) estTime := elapsed / time.Duration(synced) * time.Duration(estBytes) From a487729d83ece12482eb1e7caf4ab1889ca68315 Mon Sep 17 00:00:00 2001 From: Delweng Date: Thu, 17 Jul 2025 11:07:22 +0800 Subject: [PATCH 24/56] triedb/pathdb: improve the performance of parse index block (#32219) The implementation of `parseIndexBlock` used a reverse loop with slice appends to build the restart points, which was less cache-friendly and involved unnecessary allocations and operations. In this PR we change the implementation to read and validate the restart points in one single forward loop. Here is the benchmark test: ```bash go test -benchmem -bench=BenchmarkParseIndexBlock ./triedb/pathdb/ ``` The result as below: ``` benchmark old ns/op new ns/op delta BenchmarkParseIndexBlock-8 52.9 37.5 -29.05% ``` about 29% improvements --------- Signed-off-by: jsvisa --- triedb/pathdb/history_index_block.go | 33 +++++++++++------------ triedb/pathdb/history_index_block_test.go | 18 +++++++++++++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/triedb/pathdb/history_index_block.go b/triedb/pathdb/history_index_block.go index 7b85667e9d..10cc88ed4e 100644 --- a/triedb/pathdb/history_index_block.go +++ b/triedb/pathdb/history_index_block.go @@ -116,34 +116,31 @@ func parseIndexBlock(blob []byte) ([]uint16, []byte, error) { if len(blob) < 1 { return nil, nil, fmt.Errorf("corrupted index block, len: %d", len(blob)) } - restartLen := blob[len(blob)-1] + restartLen := int(blob[len(blob)-1]) if restartLen == 0 { return nil, nil, errors.New("corrupted index block, no restart") } - tailLen := int(restartLen)*2 + 1 + tailLen := restartLen*2 + 1 if len(blob) < tailLen { return nil, nil, fmt.Errorf("truncated restarts, size: %d, restarts: %d", len(blob), restartLen) } - restarts := make([]uint16, 0, restartLen) - for i := int(restartLen); i > 0; i-- { - restart := binary.BigEndian.Uint16(blob[len(blob)-1-2*i:]) - restarts = append(restarts, restart) - } - // Validate that restart points are strictly ordered and within the valid + restarts := make([]uint16, restartLen) + dataEnd := len(blob) - tailLen + + // Extract and validate that restart points are strictly ordered and within the valid // data range. - var prev uint16 - for i := 0; i < len(restarts); i++ { - if i != 0 { - if restarts[i] <= prev { - return nil, nil, fmt.Errorf("restart out of order, prev: %d, next: %d", prev, restarts[i]) - } + for i := 0; i < restartLen; i++ { + off := dataEnd + 2*i + restarts[i] = binary.BigEndian.Uint16(blob[off : off+2]) + + if i > 0 && restarts[i] <= restarts[i-1] { + return nil, nil, fmt.Errorf("restart out of order, prev: %d, next: %d", restarts[i-1], restarts[i]) } - if int(restarts[i]) >= len(blob)-tailLen { - return nil, nil, fmt.Errorf("invalid restart position, restart: %d, size: %d", restarts[i], len(blob)-tailLen) + if int(restarts[i]) >= dataEnd { + return nil, nil, fmt.Errorf("invalid restart position, restart: %d, size: %d", restarts[i], dataEnd) } - prev = restarts[i] } - return restarts, blob[:len(blob)-tailLen], nil + return restarts, blob[:dataEnd], nil } // blockReader is the reader to access the element within a block. diff --git a/triedb/pathdb/history_index_block_test.go b/triedb/pathdb/history_index_block_test.go index 173387b447..7b0e362c66 100644 --- a/triedb/pathdb/history_index_block_test.go +++ b/triedb/pathdb/history_index_block_test.go @@ -214,3 +214,21 @@ func TestCorruptedIndexBlock(t *testing.T) { t.Fatal("Corrupted index block data is not detected") } } + +// BenchmarkParseIndexBlock benchmarks the performance of parseIndexBlock. +func BenchmarkParseIndexBlock(b *testing.B) { + // Generate a realistic index block blob + bw, _ := newBlockWriter(nil, newIndexBlockDesc(0)) + for i := 0; i < 4096; i++ { + bw.append(uint64(i * 2)) + } + blob := bw.finish() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, err := parseIndexBlock(blob) + if err != nil { + b.Fatalf("parseIndexBlock failed: %v", err) + } + } +} From 0dacfef8ac42e7be5db26c2956f2b238ba7c75e8 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Thu, 17 Jul 2025 11:19:20 +0800 Subject: [PATCH 25/56] all: define constructor for BlobSidecar (#32213) The main purpose of this change is to enforce the version setting when constructing the blobSidecar, avoiding creating sidecar with wrong/default version tag. --- beacon/engine/types_test.go | 12 +---- cmd/devp2p/internal/ethtest/suite.go | 16 ++---- core/txpool/blobpool/blobpool_test.go | 12 +---- core/txpool/validation.go | 4 +- core/types/tx_blob.go | 73 ++++++++++++++++++++++----- core/types/tx_blob_test.go | 6 +-- eth/catalyst/api.go | 8 ++- eth/catalyst/api_test.go | 9 +--- eth/protocols/eth/handler_test.go | 6 +-- ethclient/simulated/backend_test.go | 6 +-- internal/ethapi/api.go | 6 +-- internal/ethapi/api_test.go | 24 +++------ internal/ethapi/transaction_args.go | 7 +-- miner/worker.go | 15 ++---- signer/core/apitypes/types.go | 9 ++-- signer/core/apitypes/types_test.go | 6 +-- 16 files changed, 97 insertions(+), 122 deletions(-) diff --git a/beacon/engine/types_test.go b/beacon/engine/types_test.go index ff376dfd2e..5716a19627 100644 --- a/beacon/engine/types_test.go +++ b/beacon/engine/types_test.go @@ -34,21 +34,13 @@ func TestBlobs(t *testing.T) { header := types.Header{} block := types.NewBlock(&header, &types.Body{}, nil, nil) - sidecarWithoutCellProofs := &types.BlobTxSidecar{ - Blobs: []kzg4844.Blob{*emptyBlob}, - Commitments: []kzg4844.Commitment{emptyBlobCommit}, - Proofs: []kzg4844.Proof{emptyBlobProof}, - } + sidecarWithoutCellProofs := types.NewBlobTxSidecar(types.BlobSidecarVersion0, []kzg4844.Blob{*emptyBlob}, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}) env := BlockToExecutableData(block, common.Big0, []*types.BlobTxSidecar{sidecarWithoutCellProofs}, nil) if len(env.BlobsBundle.Proofs) != 1 { t.Fatalf("Expect 1 proof in blobs bundle, got %v", len(env.BlobsBundle.Proofs)) } - sidecarWithCellProofs := &types.BlobTxSidecar{ - Blobs: []kzg4844.Blob{*emptyBlob}, - Commitments: []kzg4844.Commitment{emptyBlobCommit}, - Proofs: emptyCellProof, - } + sidecarWithCellProofs := types.NewBlobTxSidecar(types.BlobSidecarVersion0, []kzg4844.Blob{*emptyBlob}, []kzg4844.Commitment{emptyBlobCommit}, emptyCellProof) env = BlockToExecutableData(block, common.Big0, []*types.BlobTxSidecar{sidecarWithCellProofs}, nil) if len(env.BlobsBundle.Proofs) != 128 { t.Fatalf("Expect 128 proofs in blobs bundle, got %v", len(env.BlobsBundle.Proofs)) diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 97e8fd84fa..b5a346c074 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -879,11 +879,7 @@ func makeSidecar(data ...byte) *types.BlobTxSidecar { commitments = append(commitments, c) proofs = append(proofs, p) } - return &types.BlobTxSidecar{ - Blobs: blobs, - Commitments: commitments, - Proofs: proofs, - } + return types.NewBlobTxSidecar(types.BlobSidecarVersion0, blobs, commitments, proofs) } func (s *Suite) makeBlobTxs(count, blobs int, discriminator byte) (txs types.Transactions) { @@ -988,14 +984,10 @@ func (s *Suite) TestBlobViolations(t *utesting.T) { // data has been modified to produce a different commitment hash. func mangleSidecar(tx *types.Transaction) *types.Transaction { sidecar := tx.BlobTxSidecar() - copy := types.BlobTxSidecar{ - Blobs: append([]kzg4844.Blob{}, sidecar.Blobs...), - Commitments: append([]kzg4844.Commitment{}, sidecar.Commitments...), - Proofs: append([]kzg4844.Proof{}, sidecar.Proofs...), - } + cpy := sidecar.Copy() // zero the first commitment to alter the sidecar hash - copy.Commitments[0] = kzg4844.Commitment{} - return tx.WithBlobTxSidecar(©) + cpy.Commitments[0] = kzg4844.Commitment{} + return tx.WithBlobTxSidecar(cpy) } func (s *Suite) TestBlobTxWithoutSidecar(t *utesting.T) { diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index f8d3f38412..6a66c03c62 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -238,11 +238,7 @@ func makeMultiBlobTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCa BlobFeeCap: uint256.NewInt(blobFeeCap), BlobHashes: blobHashes, Value: uint256.NewInt(100), - Sidecar: &types.BlobTxSidecar{ - Blobs: blobs, - Commitments: commitments, - Proofs: proofs, - }, + Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, blobs, commitments, proofs), } return types.MustSignNewTx(key, types.LatestSigner(params.MainnetChainConfig), blobtx) } @@ -265,11 +261,7 @@ func makeUnsignedTxWithTestBlob(nonce uint64, gasTipCap uint64, gasFeeCap uint64 BlobFeeCap: uint256.NewInt(blobFeeCap), BlobHashes: []common.Hash{testBlobVHashes[blobIdx]}, Value: uint256.NewInt(100), - Sidecar: &types.BlobTxSidecar{ - Blobs: []kzg4844.Blob{*testBlobs[blobIdx]}, - Commitments: []kzg4844.Commitment{testBlobCommits[blobIdx]}, - Proofs: []kzg4844.Proof{testBlobProofs[blobIdx]}, - }, + Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, []kzg4844.Blob{*testBlobs[blobIdx]}, []kzg4844.Commitment{testBlobCommits[blobIdx]}, []kzg4844.Proof{testBlobProofs[blobIdx]}), } } diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 92381fd76c..80ba994d1a 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -185,7 +185,7 @@ func validateBlobTx(tx *types.Transaction, head *types.Header, opts *ValidationO } func validateBlobSidecarLegacy(sidecar *types.BlobTxSidecar, hashes []common.Hash) error { - if sidecar.Version != 0 { + if sidecar.Version != types.BlobSidecarVersion0 { return fmt.Errorf("invalid sidecar version pre-osaka: %v", sidecar.Version) } if len(sidecar.Proofs) != len(hashes) { @@ -200,7 +200,7 @@ func validateBlobSidecarLegacy(sidecar *types.BlobTxSidecar, hashes []common.Has } func validateBlobSidecarOsaka(sidecar *types.BlobTxSidecar, hashes []common.Hash) error { - if sidecar.Version != 1 { + if sidecar.Version != types.BlobSidecarVersion1 { return fmt.Errorf("invalid sidecar version post-osaka: %v", sidecar.Version) } if len(sidecar.Proofs) != len(hashes)*kzg4844.CellProofsPerBlob { diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index 774d6d14d2..9dd76c7f9d 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -31,6 +31,18 @@ import ( "github.com/holiman/uint256" ) +const ( + // BlobSidecarVersion0 includes a single proof for verifying the entire blob + // against its commitment. Used when the full blob is available and needs to + // be checked as a whole. + BlobSidecarVersion0 = byte(0) + + // BlobSidecarVersion1 includes multiple cell proofs for verifying specific + // blob elements (cells). Used in scenarios like data availability sampling, + // where only portions of the blob are verified individually. + BlobSidecarVersion1 = byte(1) +) + // BlobTx represents an EIP-4844 transaction. type BlobTx struct { ChainID *uint256.Int @@ -63,6 +75,16 @@ type BlobTxSidecar struct { Proofs []kzg4844.Proof // Proofs needed by the blob pool } +// NewBlobTxSidecar initialises the BlobTxSidecar object with the provided parameters. +func NewBlobTxSidecar(version byte, blobs []kzg4844.Blob, commitments []kzg4844.Commitment, proofs []kzg4844.Proof) *BlobTxSidecar { + return &BlobTxSidecar{ + Version: version, + Blobs: blobs, + Commitments: commitments, + Proofs: proofs, + } +} + // BlobHashes computes the blob hashes of the given blobs. func (sc *BlobTxSidecar) BlobHashes() []common.Hash { hasher := sha256.New() @@ -76,7 +98,7 @@ func (sc *BlobTxSidecar) BlobHashes() []common.Hash { // CellProofsAt returns the cell proofs for blob with index idx. // This method is only valid for sidecars with version 1. func (sc *BlobTxSidecar) CellProofsAt(idx int) ([]kzg4844.Proof, error) { - if sc.Version != 1 { + if sc.Version != BlobSidecarVersion1 { return nil, fmt.Errorf("cell proof unsupported, version: %d", sc.Version) } if idx < 0 || idx >= len(sc.Blobs) { @@ -89,6 +111,25 @@ func (sc *BlobTxSidecar) CellProofsAt(idx int) ([]kzg4844.Proof, error) { return sc.Proofs[index : index+kzg4844.CellProofsPerBlob], nil } +// ToV1 converts the BlobSidecar to version 1, attaching the cell proofs. +func (sc *BlobTxSidecar) ToV1() error { + if sc.Version == BlobSidecarVersion1 { + return nil + } + if sc.Version == BlobSidecarVersion0 { + sc.Proofs = make([]kzg4844.Proof, 0, len(sc.Blobs)*kzg4844.CellProofsPerBlob) + for _, blob := range sc.Blobs { + cellProofs, err := kzg4844.ComputeCellProofs(&blob) + if err != nil { + return err + } + sc.Proofs = append(sc.Proofs, cellProofs...) + } + sc.Version = BlobSidecarVersion1 + } + return nil +} + // encodedSize computes the RLP size of the sidecar elements. This does NOT return the // encoded size of the BlobTxSidecar, it's just a helper for tx.Size(). func (sc *BlobTxSidecar) encodedSize() uint64 { @@ -121,6 +162,19 @@ func (sc *BlobTxSidecar) ValidateBlobCommitmentHashes(hashes []common.Hash) erro return nil } +// Copy returns a deep-copied BlobTxSidecar object. +func (sc *BlobTxSidecar) Copy() *BlobTxSidecar { + return &BlobTxSidecar{ + Version: sc.Version, + + // The element of these slice is fix-size byte array, + // therefore slices.Clone will actually deep copy by value. + Blobs: slices.Clone(sc.Blobs), + Commitments: slices.Clone(sc.Commitments), + Proofs: slices.Clone(sc.Proofs), + } +} + // blobTxWithBlobs represents blob tx with its corresponding sidecar. // This is an interface because sidecars are versioned. type blobTxWithBlobs interface { @@ -148,7 +202,7 @@ func (btx *blobTxWithBlobsV0) tx() *BlobTx { } func (btx *blobTxWithBlobsV0) assign(sc *BlobTxSidecar) error { - sc.Version = 0 + sc.Version = BlobSidecarVersion0 sc.Blobs = btx.Blobs sc.Commitments = btx.Commitments sc.Proofs = btx.Proofs @@ -160,10 +214,10 @@ func (btx *blobTxWithBlobsV1) tx() *BlobTx { } func (btx *blobTxWithBlobsV1) assign(sc *BlobTxSidecar) error { - if btx.Version != 1 { + if btx.Version != BlobSidecarVersion1 { return fmt.Errorf("unsupported blob tx version %d", btx.Version) } - sc.Version = 1 + sc.Version = BlobSidecarVersion1 sc.Blobs = btx.Blobs sc.Commitments = btx.Commitments sc.Proofs = btx.Proofs @@ -217,12 +271,7 @@ func (tx *BlobTx) copy() TxData { cpy.S.Set(tx.S) } if tx.Sidecar != nil { - cpy.Sidecar = &BlobTxSidecar{ - Version: tx.Sidecar.Version, - Blobs: slices.Clone(tx.Sidecar.Blobs), - Commitments: slices.Clone(tx.Sidecar.Commitments), - Proofs: slices.Clone(tx.Sidecar.Proofs), - } + cpy.Sidecar = tx.Sidecar.Copy() } return cpy } @@ -280,7 +329,7 @@ func (tx *BlobTx) encode(b *bytes.Buffer) error { case tx.Sidecar == nil: return rlp.Encode(b, tx) - case tx.Sidecar.Version == 0: + case tx.Sidecar.Version == BlobSidecarVersion0: return rlp.Encode(b, &blobTxWithBlobsV0{ BlobTx: tx, Blobs: tx.Sidecar.Blobs, @@ -288,7 +337,7 @@ func (tx *BlobTx) encode(b *bytes.Buffer) error { Proofs: tx.Sidecar.Proofs, }) - case tx.Sidecar.Version == 1: + case tx.Sidecar.Version == BlobSidecarVersion1: return rlp.Encode(b, &blobTxWithBlobsV1{ BlobTx: tx, Version: tx.Sidecar.Version, diff --git a/core/types/tx_blob_test.go b/core/types/tx_blob_test.go index b9e6dcb0bb..3b368456a4 100644 --- a/core/types/tx_blob_test.go +++ b/core/types/tx_blob_test.go @@ -87,11 +87,7 @@ func createEmptyBlobTx(key *ecdsa.PrivateKey, withSidecar bool) *Transaction { } func createEmptyBlobTxInner(withSidecar bool) *BlobTx { - sidecar := &BlobTxSidecar{ - Blobs: []kzg4844.Blob{*emptyBlob}, - Commitments: []kzg4844.Commitment{emptyBlobCommit}, - Proofs: []kzg4844.Proof{emptyBlobProof}, - } + sidecar := NewBlobTxSidecar(BlobSidecarVersion0, []kzg4844.Blob{*emptyBlob}, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}) blobtx := &BlobTx{ ChainID: uint256.NewInt(1), Nonce: 5, diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index c52ffb09ba..f91896cc6e 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -527,10 +527,10 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo if len(hashes) > 128 { return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))) } - available := api.eth.BlobTxPool().AvailableBlobs(hashes) getBlobsRequestedCounter.Inc(int64(len(hashes))) getBlobsAvailableCounter.Inc(int64(available)) + // Optimization: check first if all blobs are available, if not, return empty response if available != len(hashes) { getBlobsV2RequestMiss.Inc(1) @@ -557,7 +557,7 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo // not found, return empty response return nil, nil } - if sidecar.Version != 1 { + if sidecar.Version != types.BlobSidecarVersion1 { log.Info("GetBlobs queried V0 transaction: index %v, blobhashes %v", index, sidecar.BlobHashes()) return nil, nil } @@ -566,9 +566,7 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo if idxes, ok := index[hash]; ok { proofs, err := sidecar.CellProofsAt(bIdx) if err != nil { - // TODO @rjl @marius we should return an error - log.Info("Failed to get cell proof", "err", err) - return nil, nil + return nil, engine.InvalidParams.With(err) } var cellProofs []hexutil.Bytes for _, proof := range proofs { diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index cb6ae053b6..dc7967ba2e 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -1511,13 +1511,8 @@ func TestBlockToPayloadWithBlobs(t *testing.T) { } txs = append(txs, types.NewTx(&inner)) - sidecars := []*types.BlobTxSidecar{ - { - Blobs: make([]kzg4844.Blob, 1), - Commitments: make([]kzg4844.Commitment, 1), - Proofs: make([]kzg4844.Proof, 1), - }, - } + sidecar := types.NewBlobTxSidecar(types.BlobSidecarVersion0, make([]kzg4844.Blob, 1), make([]kzg4844.Commitment, 1), make([]kzg4844.Proof, 1)) + sidecars := []*types.BlobTxSidecar{sidecar} block := types.NewBlock(&header, &types.Body{Transactions: txs}, nil, trie.NewStackTrie(nil)) envelope := engine.BlockToExecutableData(block, nil, sidecars, nil) diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index a3419d96ff..65c491f815 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -661,11 +661,7 @@ func testGetPooledTransaction(t *testing.T, blobTx bool) { To: testAddr, BlobHashes: []common.Hash{emptyBlobHash}, BlobFeeCap: uint256.MustFromBig(common.Big1), - Sidecar: &types.BlobTxSidecar{ - Blobs: emptyBlobs, - Commitments: []kzg4844.Commitment{emptyBlobCommit}, - Proofs: []kzg4844.Proof{emptyBlobProof}, - }, + Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, emptyBlobs, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}), }) if err != nil { t.Fatal(err) diff --git a/ethclient/simulated/backend_test.go b/ethclient/simulated/backend_test.go index 303e480a09..7a399d41f3 100644 --- a/ethclient/simulated/backend_test.go +++ b/ethclient/simulated/backend_test.go @@ -83,11 +83,7 @@ func newBlobTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) To: addr, AccessList: nil, BlobHashes: []common.Hash{testBlobVHash}, - Sidecar: &types.BlobTxSidecar{ - Blobs: []kzg4844.Blob{*testBlob}, - Commitments: []kzg4844.Commitment{testBlobCommit}, - Proofs: []kzg4844.Proof{testBlobProof}, - }, + Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, []kzg4844.Blob{*testBlob}, []kzg4844.Commitment{testBlobCommit}, []kzg4844.Proof{testBlobProof}), }) return types.SignTx(tx, types.LatestSignerForChainID(chainid), key) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 8857f1c04f..51b6ca3c44 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1603,11 +1603,7 @@ func (api *TransactionAPI) SignTransaction(ctx context.Context, args Transaction // no longer retains the blobs, only the blob hashes. In this step, we need // to put back the blob(s). if args.IsEIP4844() { - signed = signed.WithBlobTxSidecar(&types.BlobTxSidecar{ - Blobs: args.Blobs, - Commitments: args.Commitments, - Proofs: args.Proofs, - }) + signed = signed.WithBlobTxSidecar(types.NewBlobTxSidecar(types.BlobSidecarVersion0, args.Blobs, args.Commitments, args.Proofs)) } data, err := signed.MarshalBinary() if err != nil { diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 1c9be836fc..de6d1d5e06 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -2758,12 +2758,8 @@ func TestFillBlobTransaction(t *testing.T) { Proofs: []kzg4844.Proof{emptyBlobProof}, }, want: &result{ - Hashes: []common.Hash{emptyBlobHash}, - Sidecar: &types.BlobTxSidecar{ - Blobs: emptyBlobs, - Commitments: []kzg4844.Commitment{emptyBlobCommit}, - Proofs: []kzg4844.Proof{emptyBlobProof}, - }, + Hashes: []common.Hash{emptyBlobHash}, + Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, emptyBlobs, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}), }, }, { @@ -2778,12 +2774,8 @@ func TestFillBlobTransaction(t *testing.T) { Proofs: []kzg4844.Proof{emptyBlobProof}, }, want: &result{ - Hashes: []common.Hash{emptyBlobHash}, - Sidecar: &types.BlobTxSidecar{ - Blobs: emptyBlobs, - Commitments: []kzg4844.Commitment{emptyBlobCommit}, - Proofs: []kzg4844.Proof{emptyBlobProof}, - }, + Hashes: []common.Hash{emptyBlobHash}, + Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, emptyBlobs, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}), }, }, { @@ -2808,12 +2800,8 @@ func TestFillBlobTransaction(t *testing.T) { Blobs: emptyBlobs, }, want: &result{ - Hashes: []common.Hash{emptyBlobHash}, - Sidecar: &types.BlobTxSidecar{ - Blobs: emptyBlobs, - Commitments: []kzg4844.Commitment{emptyBlobCommit}, - Proofs: []kzg4844.Proof{emptyBlobProof}, - }, + Hashes: []common.Hash{emptyBlobHash}, + Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, emptyBlobs, []kzg4844.Commitment{emptyBlobCommit}, []kzg4844.Proof{emptyBlobProof}), }, }, } diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 7a7d63c535..6b094721e4 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -527,11 +527,8 @@ func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction { BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)), } if args.Blobs != nil { - data.(*types.BlobTx).Sidecar = &types.BlobTxSidecar{ - Blobs: args.Blobs, - Commitments: args.Commitments, - Proofs: args.Proofs, - } + // TODO(rjl493456442, marius) support V1 + data.(*types.BlobTx).Sidecar = types.NewBlobTxSidecar(types.BlobSidecarVersion0, args.Blobs, args.Commitments, args.Proofs) } case types.DynamicFeeTxType: diff --git a/miner/worker.go b/miner/worker.go index 21e73058ce..ee31a65359 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -32,7 +32,6 @@ import ( "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -430,17 +429,13 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran // Make sure all transactions after osaka have cell proofs if isOsaka { if sidecar := tx.BlobTxSidecar(); sidecar != nil { - if sidecar.Version == 0 { + if sidecar.Version == types.BlobSidecarVersion0 { log.Info("Including blob tx with v0 sidecar, recomputing proofs", "hash", ltx.Hash) - sidecar.Proofs = make([]kzg4844.Proof, 0, len(sidecar.Blobs)*kzg4844.CellProofsPerBlob) - for _, blob := range sidecar.Blobs { - cellProofs, err := kzg4844.ComputeCellProofs(&blob) - if err != nil { - panic(err) - } - sidecar.Proofs = append(sidecar.Proofs, cellProofs...) + if err := sidecar.ToV1(); err != nil { + txs.Pop() + log.Warn("Failed to recompute cell proofs", "hash", ltx.Hash, "err", err) + continue } - sidecar.Version = 1 } } } diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 9a32312d46..66c750a9c3 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -167,11 +167,8 @@ func (args *SendTxArgs) ToTransaction() (*types.Transaction, error) { BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)), } if args.Blobs != nil { - data.(*types.BlobTx).Sidecar = &types.BlobTxSidecar{ - Blobs: args.Blobs, - Commitments: args.Commitments, - Proofs: args.Proofs, - } + // TODO(rjl493456442, marius) support V1 + data.(*types.BlobTx).Sidecar = types.NewBlobTxSidecar(types.BlobSidecarVersion0, args.Blobs, args.Commitments, args.Proofs) } case args.MaxFeePerGas != nil: @@ -222,7 +219,6 @@ func (args *SendTxArgs) validateTxSidecar() error { return nil } - n := len(args.Blobs) // Assume user provides either only blobs (w/o hashes), or // blobs together with commitments and proofs. if args.Commitments == nil && args.Proofs != nil { @@ -232,6 +228,7 @@ func (args *SendTxArgs) validateTxSidecar() error { } // len(blobs) == len(commitments) == len(proofs) == len(hashes) + n := len(args.Blobs) if args.Commitments != nil && len(args.Commitments) != n { return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n) } diff --git a/signer/core/apitypes/types_test.go b/signer/core/apitypes/types_test.go index 22bbeba19e..ab9d1b22d8 100644 --- a/signer/core/apitypes/types_test.go +++ b/signer/core/apitypes/types_test.go @@ -129,11 +129,7 @@ func TestBlobTxs(t *testing.T) { BlobFeeCap: uint256.NewInt(700), BlobHashes: []common.Hash{hash}, Value: uint256.NewInt(100), - Sidecar: &types.BlobTxSidecar{ - Blobs: []kzg4844.Blob{blob}, - Commitments: []kzg4844.Commitment{commitment}, - Proofs: []kzg4844.Proof{proof}, - }, + Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion0, []kzg4844.Blob{blob}, []kzg4844.Commitment{commitment}, []kzg4844.Proof{proof}), } tx := types.NewTx(b) data, err := json.Marshal(tx) From b4b4068fe7414dc08a0e19fd880f3c54cd15d88b Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 18 Jul 2025 07:22:59 +0200 Subject: [PATCH 26/56] params: update tx gas limit cap (#32230) Updates the tx gas limit cap to the new parameter (2^24) https://github.com/ethereum/EIPs/pull/9986/files --- core/state_processor_test.go | 15 +++++++++++---- params/protocol_params.go | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index d175b5eac5..9d6cbdbc8b 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -132,6 +132,7 @@ func TestStateProcessorErrors(t *testing.T) { bigNumber := new(big.Int).SetBytes(common.MaxHash.Bytes()) tooBigNumber := new(big.Int).Set(bigNumber) tooBigNumber.Add(tooBigNumber, common.Big1) + gasLimit := blockchain.CurrentHeader().GasLimit for i, tt := range []struct { txs []*types.Transaction want string @@ -157,9 +158,9 @@ func TestStateProcessorErrors(t *testing.T) { }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), gasLimit+1, big.NewInt(875000000), nil), }, - want: "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached", + want: "could not apply tx 0 [0xd0fb3ea181e800cd55c4637c55c1f2f78137efb6bb9723e50bda3cad97208db2]: gas limit reached", }, { // ErrInsufficientFundsForTransfer txs: []*types.Transaction{ @@ -185,9 +186,9 @@ func TestStateProcessorErrors(t *testing.T) { }, { // ErrGasLimitReached txs: []*types.Transaction{ - makeTx(key1, 0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil), + makeTx(key1, 0, common.Address{}, big.NewInt(0), gasLimit+1, big.NewInt(875000000), nil), }, - want: "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached", + want: "could not apply tx 0 [0xd0fb3ea181e800cd55c4637c55c1f2f78137efb6bb9723e50bda3cad97208db2]: gas limit reached", }, { // ErrFeeCapTooLow txs: []*types.Transaction{ @@ -256,6 +257,12 @@ func TestStateProcessorErrors(t *testing.T) { }, // ErrSetCodeTxCreate cannot be tested here: it is impossible to create a SetCode-tx with nil `to`. // The EstimateGas API tests test this case. + { // ErrGasLimitTooHigh + txs: []*types.Transaction{ + makeTx(key1, 0, common.Address{}, big.NewInt(0), params.MaxTxGas+1, big.NewInt(875000000), nil), + }, + want: "could not apply tx 0 [0x16505812a6da0b0150593e4d4eb90190ba64816a04b27d19ca926ebd6aff8aa0]: transaction gas limit too high (cap: 16777216, tx: 16777217)", + }, } { block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config, false) _, err := blockchain.InsertChain(types.Blocks{block}) diff --git a/params/protocol_params.go b/params/protocol_params.go index f52bb27380..3b48709a88 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -28,7 +28,7 @@ const ( MaxGasLimit uint64 = 0x7fffffffffffffff // Maximum the gas limit (2^63-1). GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. - MaxTxGas uint64 = 30_000_000 // Maximum transaction gas limit after eip-7825. + MaxTxGas uint64 = 1 << 24 // Maximum transaction gas limit after eip-7825 (16,777,216). MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis. ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. From f17df6db91c5dd504dcc746d3734ae612fbd9453 Mon Sep 17 00:00:00 2001 From: kourin Date: Fri, 18 Jul 2025 19:36:10 +0900 Subject: [PATCH 27/56] core/txpool/blobpool: remove unused `txValidationFn` from BlobPool (#32237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR removes the now‑unused `txValidationFn` field from BlobPool. It became obsolete after a PR  https://github.com/ethereum/go-ethereum/pull/31202 was merged. Resolves https://github.com/ethereum/go-ethereum/issues/32236 --- core/txpool/blobpool/blobpool.go | 5 ----- core/txpool/blobpool/blobpool_test.go | 6 ------ 2 files changed, 11 deletions(-) diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index dbf65033f8..40429853f7 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -326,10 +326,6 @@ type BlobPool struct { discoverFeed event.Feed // Event feed to send out new tx events on pool discovery (reorg excluded) insertFeed event.Feed // Event feed to send out new tx events on pool inclusion (reorg included) - // txValidationFn defaults to txpool.ValidateTransaction, but can be - // overridden for testing purposes. - txValidationFn txpool.ValidationFunction - lock sync.RWMutex // Mutex protecting the pool during reorg handling } @@ -348,7 +344,6 @@ func New(config Config, chain BlockChain, hasPendingAuth func(common.Address) bo lookup: newLookup(), index: make(map[common.Address][]*blobTxMeta), spent: make(map[common.Address]*uint256.Int), - txValidationFn: txpool.ValidateTransaction, } } diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 6a66c03c62..ec1de6ef5d 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -1724,12 +1724,6 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) { // Make the pool not use disk (just drop everything). This test never reads // back the data, it just iterates over the pool in-memory items pool.store = &fakeBilly{pool.store, 0} - // Avoid validation - verifying all blob proofs take significant time - // when the capacity is large. The purpose of this bench is to measure assembling - // the lazies, not the kzg verifications. - pool.txValidationFn = func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *txpool.ValidationOptions) error { - return nil // accept all - } // Fill the pool up with one random transaction from each account with the // same price and everything to maximize the worst case scenario for i := 0; i < int(capacity); i++ { From f37fe6750f98c551091c774d08ec9bcd58852663 Mon Sep 17 00:00:00 2001 From: Delweng Date: Mon, 21 Jul 2025 16:30:43 +0800 Subject: [PATCH 28/56] triedb/pathdb: fix incorrect address length in history searching (#32248) We should use account length to check address, else OOB maybe occured Signed-off-by: jsvisa --- triedb/pathdb/history_reader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triedb/pathdb/history_reader.go b/triedb/pathdb/history_reader.go index 8f72d4053e..d0ecdf035f 100644 --- a/triedb/pathdb/history_reader.go +++ b/triedb/pathdb/history_reader.go @@ -210,7 +210,7 @@ func (r *historyReader) readAccountMetadata(address common.Address, historyID ui n := len(blob) / accountIndexSize pos := sort.Search(n, func(i int) bool { - h := blob[accountIndexSize*i : accountIndexSize*i+common.HashLength] + h := blob[accountIndexSize*i : accountIndexSize*i+common.AddressLength] return bytes.Compare(h, address.Bytes()) >= 0 }) if pos == n { From d80094f78898bf9b43b6d43c3b5bc6645fb9fe6f Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 21 Jul 2025 12:29:55 +0200 Subject: [PATCH 29/56] core/vm: triple modexp cost post-cancun (#32231) https://github.com/ethereum/EIPs/pull/9969/files --- core/vm/contracts.go | 4 ++- .../testdata/precompiles/modexp_eip7883.json | 26 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 91ff93dff4..b65dff602c 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -477,7 +477,9 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { gas.Mul(gas, adjExpLen) } // 2. Different divisor (`GQUADDIVISOR`) (3) - gas.Div(gas, big3) + if !c.eip7883 { + gas.Div(gas, big3) + } if gas.BitLen() > 64 { return math.MaxUint64 } diff --git a/core/vm/testdata/precompiles/modexp_eip7883.json b/core/vm/testdata/precompiles/modexp_eip7883.json index 62cedda66f..85e9ad1849 100644 --- a/core/vm/testdata/precompiles/modexp_eip7883.json +++ b/core/vm/testdata/precompiles/modexp_eip7883.json @@ -17,91 +17,91 @@ "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", "Expected": "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2", "Name": "nagydani-1-pow0x10001", - "Gas": 682, + "Gas": 2048, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", "Expected": "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", "Name": "nagydani-2-square", - "Gas": 500, + "Gas": 512, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", "Expected": "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", "Name": "nagydani-2-qube", - "Gas": 500, + "Gas": 512, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", "Expected": "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", "Name": "nagydani-2-pow0x10001", - "Gas": 2730, + "Gas": 8192, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", "Expected": "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", "Name": "nagydani-3-square", - "Gas": 682, + "Gas": 2048, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", "Expected": "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", "Name": "nagydani-3-qube", - "Gas": 682, + "Gas": 2048, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", "Expected": "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", "Name": "nagydani-3-pow0x10001", - "Gas": 10922, + "Gas": 32768, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", "Expected": "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10", "Name": "nagydani-4-square", - "Gas": 2730, + "Gas": 8192, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", "Expected": "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc", "Name": "nagydani-4-qube", - "Gas": 2730, + "Gas": 8192, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", "Expected": "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963", "Name": "nagydani-4-pow0x10001", - "Gas": 43690, + "Gas": 131072, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", "Expected": "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", "Name": "nagydani-5-square", - "Gas": 10922, + "Gas": 32768, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", "Expected": "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", "Name": "nagydani-5-qube", - "Gas": 10922, + "Gas": 32768, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", "Expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", "Name": "nagydani-5-pow0x10001", - "Gas": 174762, + "Gas": 524288, "NoBenchmark": false } ] From f96f82bd6bd6bf88a10c5160e97c04b3dcafcc61 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 21 Jul 2025 16:26:24 +0200 Subject: [PATCH 30/56] core, params: add limit for max blobs in blob transaction (#32246) [EIP-7594](https://eips.ethereum.org/EIPS/eip-7594) defines a limit of max 6 blobs per transaction. We need to enforce this limit during block processing. > Additionally, a limit of 6 blobs per transaction is introduced. Clients MUST enforce this limit when validating blob transactions at submission time, when received from the network, and during block production and processing. --- core/error.go | 3 +++ core/state_transition.go | 6 +++++- core/txpool/blobpool/blobpool.go | 2 +- core/txpool/blobpool/blobpool_test.go | 4 ++-- params/protocol_params.go | 1 + 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/core/error.go b/core/error.go index b9e894bd9b..635d802863 100644 --- a/core/error.go +++ b/core/error.go @@ -118,6 +118,9 @@ var ( // ErrMissingBlobHashes is returned if a blob transaction has no blob hashes. ErrMissingBlobHashes = errors.New("blob transaction missing blob hashes") + // ErrTooManyBlobs is returned if a blob transaction exceeds the maximum number of blobs. + ErrTooManyBlobs = errors.New("blob transaction has too many blobs") + // ErrBlobTxCreate is returned if a blob transaction has no explicit to field. ErrBlobTxCreate = errors.New("blob transaction of type create") diff --git a/core/state_transition.go b/core/state_transition.go index de4558253b..681c300696 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -354,6 +354,7 @@ func (st *stateTransition) preCheck() error { } } // Check the blob version validity + isOsaka := st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time) if msg.BlobHashes != nil { // The to field of a blob tx type is mandatory, and a `BlobTx` transaction internally // has it as a non-nillable value, so any msg derived from blob transaction has it non-nil. @@ -364,6 +365,9 @@ func (st *stateTransition) preCheck() error { if len(msg.BlobHashes) == 0 { return ErrMissingBlobHashes } + if isOsaka && len(msg.BlobHashes) > params.BlobTxMaxBlobs { + return ErrTooManyBlobs + } for i, hash := range msg.BlobHashes { if !kzg4844.IsValidVersionedHash(hash[:]) { return fmt.Errorf("blob %d has invalid hash version", i) @@ -395,7 +399,7 @@ func (st *stateTransition) preCheck() error { } } // Verify tx gas limit does not exceed EIP-7825 cap. - if st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time) && msg.GasLimit > params.MaxTxGas { + if isOsaka && msg.GasLimit > params.MaxTxGas { return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit) } return st.buyGas() diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 40429853f7..078af34864 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -65,7 +65,7 @@ const ( // carry. We choose a smaller limit than the protocol-permitted MaxBlobsPerBlock // in order to ensure network and txpool stability. // Note: if you increase this, validation will fail on txMaxSize. - maxBlobsPerTx = 7 + maxBlobsPerTx = params.BlobTxMaxBlobs // maxTxsPerAccount is the maximum number of blob transactions admitted from // a single account. The limit is enforced to minimize the DoS potential of diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index ec1de6ef5d..422c35f6d2 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -1191,8 +1191,8 @@ func TestBlobCountLimit(t *testing.T) { // Attempt to add transactions. var ( - tx1 = makeMultiBlobTx(0, 1, 1000, 100, 7, key1) - tx2 = makeMultiBlobTx(0, 1, 800, 70, 8, key2) + tx1 = makeMultiBlobTx(0, 1, 1000, 100, 6, key1) + tx2 = makeMultiBlobTx(0, 1, 800, 70, 7, key2) ) errs := pool.Add([]*types.Transaction{tx1, tx2}, true) diff --git a/params/protocol_params.go b/params/protocol_params.go index 3b48709a88..2ec3a5c249 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -177,6 +177,7 @@ const ( BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size) BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs BlobTxPointEvaluationPrecompileGas = 50000 // Gas price for the point evaluation precompile. + BlobTxMaxBlobs = 6 BlobBaseCost = 1 << 13 // Base execution gas cost for a blob. HistoryServeWindow = 8192 // Number of blocks to serve historical block hashes for, EIP-2935. From 36c87a220eee1dd9530d94b5bb7d77f20b38b7e5 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 21 Jul 2025 23:20:36 +0200 Subject: [PATCH 31/56] build: update tests to fusaka-devnet-3 (#32251) --- build/checksums.txt | 6 +++--- build/ci.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index 663bf9ed63..7641b9ae62 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -1,9 +1,9 @@ # This file contains sha256 checksums of optional build dependencies. -# version:spec-tests v4.5.0 +# version:spec-tests fusaka-devnet-3%40v1.0.0 # https://github.com/ethereum/execution-spec-tests/releases -# https://github.com/ethereum/execution-spec-tests/releases/download/v4.5.0/ -58afb92a0075a2cb7c4dec1281f7cb88b21b02afbedad096b580f3f8cc14c54c fixtures_develop.tar.gz +# https://github.com/ethereum/execution-spec-tests/releases/download/fusaka-devnet-3%40v1.0.0 +576261e1280e5300c458aa9b05eccb2fec5ff80a0005940dc52fa03fdd907249 fixtures_fusaka-devnet-3.tar.gz # version:golang 1.24.4 # https://go.dev/dl/ diff --git a/build/ci.go b/build/ci.go index 58c769e0d7..5126793c3d 100644 --- a/build/ci.go +++ b/build/ci.go @@ -332,7 +332,7 @@ func doTest(cmdline []string) { // downloadSpecTestFixtures downloads and extracts the execution-spec-tests fixtures. func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string { ext := ".tar.gz" - base := "fixtures_develop" + base := "fixtures_fusaka-devnet-3" archivePath := filepath.Join(cachedir, base+ext) if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil { log.Fatal(err) From b2a0e088087ad912b72468213a16cb887efe6124 Mon Sep 17 00:00:00 2001 From: maskpp Date: Tue, 22 Jul 2025 15:03:48 +0800 Subject: [PATCH 32/56] core/types: minimize this invalid intermediate state (#32241) --- core/types/tx_blob.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index 9dd76c7f9d..bbfd3c98db 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -117,15 +117,16 @@ func (sc *BlobTxSidecar) ToV1() error { return nil } if sc.Version == BlobSidecarVersion0 { - sc.Proofs = make([]kzg4844.Proof, 0, len(sc.Blobs)*kzg4844.CellProofsPerBlob) + proofs := make([]kzg4844.Proof, 0, len(sc.Blobs)*kzg4844.CellProofsPerBlob) for _, blob := range sc.Blobs { cellProofs, err := kzg4844.ComputeCellProofs(&blob) if err != nil { return err } - sc.Proofs = append(sc.Proofs, cellProofs...) + proofs = append(proofs, cellProofs...) } sc.Version = BlobSidecarVersion1 + sc.Proofs = proofs } return nil } From 83aa643621d893ad8f9edb29fd8eb698edc02d83 Mon Sep 17 00:00:00 2001 From: maskpp Date: Tue, 22 Jul 2025 15:18:23 +0800 Subject: [PATCH 33/56] core/rawdb: downgrade log level in chain freezer (#32253) --- core/rawdb/chain_freezer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go index 4834354b22..c12f2ab8fe 100644 --- a/core/rawdb/chain_freezer.go +++ b/core/rawdb/chain_freezer.go @@ -104,7 +104,7 @@ func (f *chainFreezer) Close() error { func (f *chainFreezer) readHeadNumber(db ethdb.KeyValueReader) uint64 { hash := ReadHeadBlockHash(db) if hash == (common.Hash{}) { - log.Error("Head block is not reachable") + log.Warn("Head block is not reachable") return 0 } number, ok := ReadHeaderNumber(db, hash) From 264c06a72c8069ace8f2f2af3cf48b252bf0c9de Mon Sep 17 00:00:00 2001 From: Delweng Date: Tue, 22 Jul 2025 20:03:22 +0800 Subject: [PATCH 34/56] triedb/pathdb: use binary.append to eliminate the tmp scratch slice (#32250) `binary.AppendUvarint` offers better performance than using append directly, because it avoids unnecessary memory allocation and copying. In our case, it can increase the performance by +35.8% for the `blockWriter.append` function: ``` benchmark old ns/op new ns/op delta BenchmarkBlockWriterAppend-8 5.97 3.83 -35.80% ``` --------- Signed-off-by: jsvisa Co-authored-by: Gary Rong --- triedb/pathdb/history_index_block.go | 29 +++++++---------------- triedb/pathdb/history_index_block_test.go | 19 +++++++++++++++ 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/triedb/pathdb/history_index_block.go b/triedb/pathdb/history_index_block.go index 10cc88ed4e..7648b99226 100644 --- a/triedb/pathdb/history_index_block.go +++ b/triedb/pathdb/history_index_block.go @@ -221,17 +221,14 @@ func (br *blockReader) readGreaterThan(id uint64) (uint64, error) { type blockWriter struct { desc *indexBlockDesc // Descriptor of the block restarts []uint16 // Offsets into the data slice, marking the start of each section - scratch []byte // Buffer used for encoding full integers or value differences data []byte // Aggregated encoded data slice } func newBlockWriter(blob []byte, desc *indexBlockDesc) (*blockWriter, error) { - scratch := make([]byte, binary.MaxVarintLen64) if len(blob) == 0 { return &blockWriter{ - desc: desc, - scratch: scratch, - data: make([]byte, 0, 1024), + desc: desc, + data: make([]byte, 0, 1024), }, nil } restarts, data, err := parseIndexBlock(blob) @@ -241,7 +238,6 @@ func newBlockWriter(blob []byte, desc *indexBlockDesc) (*blockWriter, error) { return &blockWriter{ desc: desc, restarts: restarts, - scratch: scratch, data: data, // safe to own the slice }, nil } @@ -268,22 +264,14 @@ func (b *blockWriter) append(id uint64) error { // // The first element in a restart range is encoded using its // full value. - n := binary.PutUvarint(b.scratch[0:], id) - b.data = append(b.data, b.scratch[:n]...) + b.data = binary.AppendUvarint(b.data, id) } else { - // The current section is not full, append the element. // The element which is not the first one in the section // is encoded using the value difference from the preceding // element. - n := binary.PutUvarint(b.scratch[0:], id-b.desc.max) - b.data = append(b.data, b.scratch[:n]...) + b.data = binary.AppendUvarint(b.data, id-b.desc.max) } b.desc.entries++ - - // The state history ID must be greater than 0. - //if b.desc.min == 0 { - // b.desc.min = id - //} b.desc.max = id return nil } @@ -392,11 +380,10 @@ func (b *blockWriter) full() bool { // // This function is safe to be called multiple times. func (b *blockWriter) finish() []byte { - var buf []byte - for _, number := range b.restarts { - binary.BigEndian.PutUint16(b.scratch[:2], number) - buf = append(buf, b.scratch[:2]...) + buf := make([]byte, len(b.restarts)*2+1) + for i, restart := range b.restarts { + binary.BigEndian.PutUint16(buf[2*i:], restart) } - buf = append(buf, byte(len(b.restarts))) + buf[len(buf)-1] = byte(len(b.restarts)) return append(b.data, buf...) } diff --git a/triedb/pathdb/history_index_block_test.go b/triedb/pathdb/history_index_block_test.go index 7b0e362c66..c251cea2ec 100644 --- a/triedb/pathdb/history_index_block_test.go +++ b/triedb/pathdb/history_index_block_test.go @@ -232,3 +232,22 @@ func BenchmarkParseIndexBlock(b *testing.B) { } } } + +// BenchmarkBlockWriterAppend benchmarks the performance of indexblock.writer +func BenchmarkBlockWriterAppend(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + + desc := newIndexBlockDesc(0) + writer, _ := newBlockWriter(nil, desc) + + for i := 0; i < b.N; i++ { + if writer.full() { + desc = newIndexBlockDesc(0) + writer, _ = newBlockWriter(nil, desc) + } + if err := writer.append(writer.desc.max + 1); err != nil { + b.Error(err) + } + } +} From a7efdcbf095fa043390c98439e6924b69ed3f80d Mon Sep 17 00:00:00 2001 From: Micke <155267459+reallesee@users.noreply.github.com> Date: Tue, 22 Jul 2025 23:06:48 +0200 Subject: [PATCH 35/56] p2p/rlpx: optimize XOR operation using bitutil.XORBytes (#32217) Replace manual byte-by-byte XOR implementation with the optimized bitutil.XORBytes function. This improves performance by using word-sized operations on supported architectures while maintaining the same functionality. The optimized version processes data in bulk rather than one byte at a time --------- Co-authored-by: Felix Lange --- p2p/rlpx/rlpx.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/p2p/rlpx/rlpx.go b/p2p/rlpx/rlpx.go index dd14822dee..c074534d4d 100644 --- a/p2p/rlpx/rlpx.go +++ b/p2p/rlpx/rlpx.go @@ -33,6 +33,7 @@ import ( "net" "time" + "github.com/ethereum/go-ethereum/common/bitutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/rlp" @@ -676,8 +677,6 @@ func exportPubkey(pub *ecies.PublicKey) []byte { func xor(one, other []byte) (xor []byte) { xor = make([]byte, len(one)) - for i := 0; i < len(one); i++ { - xor[i] = one[i] ^ other[i] - } + bitutil.XORBytes(xor, one, other) return xor } From 3b67602c4c5e701028c7246977aabff02cce643c Mon Sep 17 00:00:00 2001 From: gzeon Date: Wed, 23 Jul 2025 12:41:37 +0900 Subject: [PATCH 36/56] eth/gasestimator: fix potential overflow (#32255) Improve binary search, preventing the potential overflow in certain L2 cases --- eth/gasestimator/gasestimator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index 98a4f74b3e..7e9d8125de 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -170,7 +170,7 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin break } } - mid := (hi + lo) / 2 + mid := lo + (hi-lo)/2 if mid > lo*2 { // Most txs don't need much higher gas limit than their gas used, and most txs don't // require near the full block limit of gas, so the selection of where to bisect the From 16117eb7cddc4584865af106d2332aa89f387d3d Mon Sep 17 00:00:00 2001 From: Delweng Date: Wed, 23 Jul 2025 15:12:55 +0800 Subject: [PATCH 37/56] triedb/pathdb: fix an deadlock in history indexer (#32260) Seems the `signal.result` was not sent back in shorten case, this will cause a deadlock. --------- Signed-off-by: jsvisa Co-authored-by: Gary Rong --- triedb/pathdb/history_indexer.go | 15 ++++--- triedb/pathdb/history_indexer_test.go | 57 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 triedb/pathdb/history_indexer_test.go diff --git a/triedb/pathdb/history_indexer.go b/triedb/pathdb/history_indexer.go index 42103fab32..054d43e946 100644 --- a/triedb/pathdb/history_indexer.go +++ b/triedb/pathdb/history_indexer.go @@ -392,16 +392,17 @@ func (i *indexIniter) run(lastID uint64) { select { case signal := <-i.interrupt: // The indexing limit can only be extended or shortened continuously. - if signal.newLastID != lastID+1 && signal.newLastID != lastID-1 { - signal.result <- fmt.Errorf("invalid history id, last: %d, got: %d", lastID, signal.newLastID) + newLastID := signal.newLastID + if newLastID != lastID+1 && newLastID != lastID-1 { + signal.result <- fmt.Errorf("invalid history id, last: %d, got: %d", lastID, newLastID) continue } - i.last.Store(signal.newLastID) // update indexing range + i.last.Store(newLastID) // update indexing range // The index limit is extended by one, update the limit without // interrupting the current background process. - if signal.newLastID == lastID+1 { - lastID = signal.newLastID + if newLastID == lastID+1 { + lastID = newLastID signal.result <- nil log.Debug("Extended state history range", "last", lastID) continue @@ -425,7 +426,9 @@ func (i *indexIniter) run(lastID uint64) { return } // Adjust the indexing target and relaunch the process - lastID = signal.newLastID + lastID = newLastID + signal.result <- nil + done, interrupt = make(chan struct{}), new(atomic.Int32) go i.index(done, interrupt, lastID) log.Debug("Shortened state history range", "last", lastID) diff --git a/triedb/pathdb/history_indexer_test.go b/triedb/pathdb/history_indexer_test.go new file mode 100644 index 0000000000..abfcafc945 --- /dev/null +++ b/triedb/pathdb/history_indexer_test.go @@ -0,0 +1,57 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package pathdb + +import ( + "testing" + "time" + + "github.com/ethereum/go-ethereum/core/rawdb" +) + +// TestHistoryIndexerShortenDeadlock tests that a call to shorten does not +// deadlock when the indexer is active. This specifically targets the case where +// signal.result must be sent to unblock the caller. +func TestHistoryIndexerShortenDeadlock(t *testing.T) { + //log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) + db := rawdb.NewMemoryDatabase() + freezer, _ := rawdb.NewStateFreezer(t.TempDir(), false, false) + defer freezer.Close() + + histories := makeHistories(100) + for i, h := range histories { + accountData, storageData, accountIndex, storageIndex := h.encode() + rawdb.WriteStateHistory(freezer, uint64(i+1), h.meta.encode(), accountIndex, storageIndex, accountData, storageData) + } + // As a workaround, assign a future block to keep the initer running indefinitely + indexer := newHistoryIndexer(db, freezer, 200) + defer indexer.close() + + done := make(chan error, 1) + go func() { + done <- indexer.shorten(200) + }() + + select { + case err := <-done: + if err != nil { + t.Fatalf("shorten returned an unexpected error: %v", err) + } + case <-time.After(2 * time.Second): + t.Fatal("timed out waiting for shorten to complete, potential deadlock") + } +} From b369a855fbb93aca7928f7cdf8a5774305a5c6c0 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 24 Jul 2025 10:43:04 +0200 Subject: [PATCH 38/56] eth/protocols/snap: add healing and syncing metrics (#32258) Adds the heal time and snap sync time to grafana --------- Co-authored-by: Gary Rong --- eth/protocols/snap/metrics.go | 3 +++ eth/protocols/snap/sync.go | 26 +++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/eth/protocols/snap/metrics.go b/eth/protocols/snap/metrics.go index 6878e5b280..6319a9b75d 100644 --- a/eth/protocols/snap/metrics.go +++ b/eth/protocols/snap/metrics.go @@ -66,4 +66,7 @@ var ( // discarded during the snap sync. largeStorageDiscardGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/storage/chunk/discard", nil) largeStorageResumedGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/storage/chunk/resume", nil) + + stateSyncTimeGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/time/statesync", nil) + stateHealTimeGauge = metrics.NewRegisteredGauge("eth/protocols/snap/sync/time/stateheal", nil) ) diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go index 84ceb9105e..cf4e494645 100644 --- a/eth/protocols/snap/sync.go +++ b/eth/protocols/snap/sync.go @@ -502,8 +502,10 @@ type Syncer struct { storageHealed uint64 // Number of storage slots downloaded during the healing stage storageHealedBytes common.StorageSize // Number of raw storage bytes persisted to disk during the healing stage - startTime time.Time // Time instance when snapshot sync started - logTime time.Time // Time instance when status was last reported + startTime time.Time // Time instance when snapshot sync started + healStartTime time.Time // Time instance when the state healing started + syncTimeOnce sync.Once // Ensure that the state sync time is uploaded only once + logTime time.Time // Time instance when status was last reported pend sync.WaitGroup // Tracks network request goroutines for graceful shutdown lock sync.RWMutex // Protects fields that can change outside of sync (peers, reqs, root) @@ -685,6 +687,14 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error { s.cleanStorageTasks() s.cleanAccountTasks() if len(s.tasks) == 0 && s.healer.scheduler.Pending() == 0 { + // State healing phase completed, record the elapsed time in metrics. + // Note: healing may be rerun in subsequent cycles to fill gaps between + // pivot states (e.g., if chain sync takes longer). + if !s.healStartTime.IsZero() { + stateHealTimeGauge.Inc(int64(time.Since(s.healStartTime))) + log.Info("State healing phase is completed", "elapsed", common.PrettyDuration(time.Since(s.healStartTime))) + s.healStartTime = time.Time{} + } return nil } // Assign all the data retrieval tasks to any free peers @@ -693,7 +703,17 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error { s.assignStorageTasks(storageResps, storageReqFails, cancel) if len(s.tasks) == 0 { - // Sync phase done, run heal phase + // State sync phase completed, record the elapsed time in metrics. + // Note: the initial state sync runs only once, regardless of whether + // a new cycle is started later. Any state differences in subsequent + // cycles will be handled by the state healer. + s.syncTimeOnce.Do(func() { + stateSyncTimeGauge.Update(int64(time.Since(s.startTime))) + log.Info("State sync phase is completed", "elapsed", common.PrettyDuration(time.Since(s.startTime))) + }) + if s.healStartTime.IsZero() { + s.healStartTime = time.Now() + } s.assignTrienodeHealTasks(trienodeHealResps, trienodeHealReqFails, cancel) s.assignBytecodeHealTasks(bytecodeHealResps, bytecodeHealReqFails, cancel) } From 29eebb5eac09adddca39dbf06d66dd321b69e0a7 Mon Sep 17 00:00:00 2001 From: nthumann Date: Mon, 28 Jul 2025 02:13:50 +0100 Subject: [PATCH 39/56] core: replace the empty fmt.Errorf with errors.New (#32274) The `errors.new` function does not require string formatting, so its performance is better than that of `fmt.Errorf`. --- core/blockchain.go | 8 ++++---- core/vm/contracts.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index d52990ec5a..0b92a94b6c 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -682,7 +682,7 @@ func (bc *BlockChain) initializeHistoryPruning(latest uint64) error { predefinedPoint := history.PrunePoints[bc.genesisBlock.Hash()] if predefinedPoint == nil || freezerTail != predefinedPoint.BlockNumber { log.Error("Chain history database is pruned with unknown configuration", "tail", freezerTail) - return fmt.Errorf("unexpected database tail") + return errors.New("unexpected database tail") } bc.historyPrunePoint.Store(predefinedPoint) return nil @@ -695,15 +695,15 @@ func (bc *BlockChain) initializeHistoryPruning(latest uint64) error { // action to happen. So just tell them how to do it. log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cfg.ChainHistoryMode.String())) log.Error(fmt.Sprintf("Run 'geth prune-history' to prune pre-merge history.")) - return fmt.Errorf("history pruning requested via configuration") + return errors.New("history pruning requested via configuration") } predefinedPoint := history.PrunePoints[bc.genesisBlock.Hash()] if predefinedPoint == nil { log.Error("Chain history pruning is not supported for this network", "genesis", bc.genesisBlock.Hash()) - return fmt.Errorf("history pruning requested for unknown network") + return errors.New("history pruning requested for unknown network") } else if freezerTail > 0 && freezerTail != predefinedPoint.BlockNumber { log.Error("Chain history database is pruned to unknown block", "tail", freezerTail) - return fmt.Errorf("unexpected database tail") + return errors.New("unexpected database tail") } bc.historyPrunePoint.Store(predefinedPoint) return nil diff --git a/core/vm/contracts.go b/core/vm/contracts.go index b65dff602c..21307ff5ac 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -515,7 +515,7 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) { } // enforce size cap for inputs if c.eip7823 && max(baseLen, expLen, modLen) > 1024 { - return nil, fmt.Errorf("one or more of base/exponent/modulus length exceeded 1024 bytes") + return nil, errors.New("one or more of base/exponent/modulus length exceeded 1024 bytes") } // Retrieve the operands and execute the exponentiation var ( From 0fe1bc071727504beb288ee665eb6efbb01fc5c1 Mon Sep 17 00:00:00 2001 From: Galoretka Date: Mon, 28 Jul 2025 04:16:47 +0300 Subject: [PATCH 40/56] eth/catalyst: fix error message in ExecuteStatelessPayloadV4 (#32269) Correct the error message in the ExecuteStatelessPayloadV4 function to reference newPayloadV4 and the Prague fork, instead of incorrectly referencing newPayloadV3 and Cancun. This improves clarity during debugging and aligns the error message with the actual function and fork being validated. No logic is changed. --------- Co-authored-by: rjl493456442 --- eth/catalyst/witness.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/catalyst/witness.go b/eth/catalyst/witness.go index 712539c5e3..703f1b0881 100644 --- a/eth/catalyst/witness.go +++ b/eth/catalyst/witness.go @@ -228,8 +228,8 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil beaconRoot post-cancun") case executionRequests == nil: return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague") - case !api.checkFork(params.Timestamp, forks.Prague): - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") + case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka): + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("newPayloadV4 must only be called for prague payloads") } requests := convertRequests(executionRequests) if err := validateRequests(requests); err != nil { From a7aed7bd6f6504b49854a445f27b9cfb4a94df51 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 28 Jul 2025 14:57:45 +0800 Subject: [PATCH 41/56] cmd, eth, internal: introduce debug_sync (#32177) Alternative implementation of https://github.com/ethereum/go-ethereum/pull/32159 --- cmd/geth/config.go | 6 +- cmd/utils/flags.go | 14 ++- eth/catalyst/tester.go | 103 ----------------- eth/downloader/beacondevsync.go | 23 +--- eth/downloader/fetchers.go | 3 - eth/syncer/syncer.go | 197 ++++++++++++++++++++++++++++++++ internal/web3ext/web3ext.go | 5 + 7 files changed, 219 insertions(+), 132 deletions(-) delete mode 100644 eth/catalyst/tester.go create mode 100644 eth/syncer/syncer.go diff --git a/cmd/geth/config.go b/cmd/geth/config.go index d7c354ff9f..96bd715e88 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -262,14 +262,16 @@ func makeFullNode(ctx *cli.Context) *node.Node { if cfg.Ethstats.URL != "" { utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL) } - // Configure full-sync tester service if requested + // Configure synchronization override service + var synctarget common.Hash if ctx.IsSet(utils.SyncTargetFlag.Name) { hex := hexutil.MustDecode(ctx.String(utils.SyncTargetFlag.Name)) if len(hex) != common.HashLength { utils.Fatalf("invalid sync target length: have %d, want %d", len(hex), common.HashLength) } - utils.RegisterFullSyncTester(stack, eth, common.BytesToHash(hex), ctx.Bool(utils.ExitWhenSyncedFlag.Name)) + synctarget = common.BytesToHash(hex) } + utils.RegisterSyncOverrideService(stack, eth, synctarget, ctx.Bool(utils.ExitWhenSyncedFlag.Name)) if ctx.IsSet(utils.DeveloperFlag.Name) { // Start dev mode. diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index b86970651f..cbc1d925e4 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -49,10 +49,10 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/eth/syncer" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/remotedb" @@ -1997,10 +1997,14 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf return filterSystem } -// RegisterFullSyncTester adds the full-sync tester service into node. -func RegisterFullSyncTester(stack *node.Node, eth *eth.Ethereum, target common.Hash, exitWhenSynced bool) { - catalyst.RegisterFullSyncTester(stack, eth, target, exitWhenSynced) - log.Info("Registered full-sync tester", "hash", target, "exitWhenSynced", exitWhenSynced) +// RegisterSyncOverrideService adds the synchronization override service into node. +func RegisterSyncOverrideService(stack *node.Node, eth *eth.Ethereum, target common.Hash, exitWhenSynced bool) { + if target != (common.Hash{}) { + log.Info("Registered sync override service", "hash", target, "exitWhenSynced", exitWhenSynced) + } else { + log.Info("Registered sync override service") + } + syncer.Register(stack, eth, target, exitWhenSynced) } // SetupMetrics configures the metrics system. diff --git a/eth/catalyst/tester.go b/eth/catalyst/tester.go deleted file mode 100644 index 10a480837e..0000000000 --- a/eth/catalyst/tester.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2022 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package catalyst - -import ( - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/node" -) - -// FullSyncTester is an auxiliary service that allows Geth to perform full sync -// alone without consensus-layer attached. Users must specify a valid block hash -// as the sync target. -// -// This tester can be applied to different networks, no matter it's pre-merge or -// post-merge, but only for full-sync. -type FullSyncTester struct { - stack *node.Node - backend *eth.Ethereum - target common.Hash - closed chan struct{} - wg sync.WaitGroup - exitWhenSynced bool -} - -// RegisterFullSyncTester registers the full-sync tester service into the node -// stack for launching and stopping the service controlled by node. -func RegisterFullSyncTester(stack *node.Node, backend *eth.Ethereum, target common.Hash, exitWhenSynced bool) (*FullSyncTester, error) { - cl := &FullSyncTester{ - stack: stack, - backend: backend, - target: target, - closed: make(chan struct{}), - exitWhenSynced: exitWhenSynced, - } - stack.RegisterLifecycle(cl) - return cl, nil -} - -// Start launches the beacon sync with provided sync target. -func (tester *FullSyncTester) Start() error { - tester.wg.Add(1) - go func() { - defer tester.wg.Done() - - // Trigger beacon sync with the provided block hash as trusted - // chain head. - err := tester.backend.Downloader().BeaconDevSync(ethconfig.FullSync, tester.target, tester.closed) - if err != nil { - log.Info("Failed to trigger beacon sync", "err", err) - } - - ticker := time.NewTicker(time.Second * 5) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - // Stop in case the target block is already stored locally. - if block := tester.backend.BlockChain().GetBlockByHash(tester.target); block != nil { - log.Info("Full-sync target reached", "number", block.NumberU64(), "hash", block.Hash()) - - if tester.exitWhenSynced { - go tester.stack.Close() // async since we need to close ourselves - log.Info("Terminating the node") - } - return - } - - case <-tester.closed: - return - } - } - }() - return nil -} - -// Stop stops the full-sync tester to stop all background activities. -// This function can only be called for one time. -func (tester *FullSyncTester) Stop() error { - close(tester.closed) - tester.wg.Wait() - return nil -} diff --git a/eth/downloader/beacondevsync.go b/eth/downloader/beacondevsync.go index 0032eb53b9..7b30684133 100644 --- a/eth/downloader/beacondevsync.go +++ b/eth/downloader/beacondevsync.go @@ -18,7 +18,6 @@ package downloader import ( "errors" - "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -34,28 +33,14 @@ import ( // Note, this must not be used in live code. If the forkchcoice endpoint where // to use this instead of giving us the payload first, then essentially nobody // in the network would have the block yet that we'd attempt to retrieve. -func (d *Downloader) BeaconDevSync(mode SyncMode, hash common.Hash, stop chan struct{}) error { +func (d *Downloader) BeaconDevSync(mode SyncMode, header *types.Header) error { // Be very loud that this code should not be used in a live node log.Warn("----------------------------------") - log.Warn("Beacon syncing with hash as target", "hash", hash) + log.Warn("Beacon syncing with hash as target", "number", header.Number, "hash", header.Hash()) log.Warn("This is unhealthy for a live node!") + log.Warn("This is incompatible with the consensus layer!") log.Warn("----------------------------------") - - log.Info("Waiting for peers to retrieve sync target") - for { - // If the node is going down, unblock - select { - case <-stop: - return errors.New("stop requested") - default: - } - header, err := d.GetHeader(hash) - if err != nil { - time.Sleep(time.Second) - continue - } - return d.BeaconSync(mode, header, header) - } + return d.BeaconSync(mode, header, header) } // GetHeader tries to retrieve the header with a given hash from a random peer. diff --git a/eth/downloader/fetchers.go b/eth/downloader/fetchers.go index 4ebb9bbc98..6e5c65eb20 100644 --- a/eth/downloader/fetchers.go +++ b/eth/downloader/fetchers.go @@ -45,9 +45,6 @@ func (d *Downloader) fetchHeadersByHash(p *peerConnection, hash common.Hash, amo defer timeoutTimer.Stop() select { - case <-d.cancelCh: - return nil, nil, errCanceled - case <-timeoutTimer.C: // Header retrieval timed out, update the metrics p.log.Debug("Header request timed out", "elapsed", ttl) diff --git a/eth/syncer/syncer.go b/eth/syncer/syncer.go new file mode 100644 index 0000000000..5c4d2401e9 --- /dev/null +++ b/eth/syncer/syncer.go @@ -0,0 +1,197 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package syncer + +import ( + "errors" + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" +) + +type syncReq struct { + hash common.Hash + errc chan error +} + +// Syncer is an auxiliary service that allows Geth to perform full sync +// alone without consensus-layer attached. Users must specify a valid block hash +// as the sync target. +// +// This tool can be applied to different networks, no matter it's pre-merge or +// post-merge, but only for full-sync. +type Syncer struct { + stack *node.Node + backend *eth.Ethereum + target common.Hash + request chan *syncReq + closed chan struct{} + wg sync.WaitGroup + exitWhenSynced bool +} + +// Register registers the synchronization override service into the node +// stack for launching and stopping the service controlled by node. +func Register(stack *node.Node, backend *eth.Ethereum, target common.Hash, exitWhenSynced bool) (*Syncer, error) { + s := &Syncer{ + stack: stack, + backend: backend, + target: target, + request: make(chan *syncReq), + closed: make(chan struct{}), + exitWhenSynced: exitWhenSynced, + } + stack.RegisterAPIs(s.APIs()) + stack.RegisterLifecycle(s) + return s, nil +} + +// APIs return the collection of RPC services the ethereum package offers. +// NOTE, some of these services probably need to be moved to somewhere else. +func (s *Syncer) APIs() []rpc.API { + return []rpc.API{ + { + Namespace: "debug", + Service: NewAPI(s), + }, + } +} + +// run is the main loop that monitors sync requests from users and initiates +// sync operations when necessary. It also checks whether the specified target +// has been reached and shuts down Geth if requested by the user. +func (s *Syncer) run() { + defer s.wg.Done() + + var ( + target *types.Header + ticker = time.NewTicker(time.Second * 5) + ) + for { + select { + case req := <-s.request: + var ( + resync bool + retries int + logged bool + ) + for { + if retries >= 10 { + req.errc <- fmt.Errorf("sync target is not avaibale, %x", req.hash) + break + } + select { + case <-s.closed: + req.errc <- errors.New("syncer closed") + return + default: + } + + header, err := s.backend.Downloader().GetHeader(req.hash) + if err != nil { + if !logged { + logged = true + log.Info("Waiting for peers to retrieve sync target", "hash", req.hash) + } + time.Sleep(time.Second * time.Duration(retries+1)) + retries++ + continue + } + if target != nil && header.Number.Cmp(target.Number) <= 0 { + req.errc <- fmt.Errorf("stale sync target, current: %d, received: %d", target.Number, header.Number) + break + } + target = header + resync = true + break + } + if resync { + req.errc <- s.backend.Downloader().BeaconDevSync(ethconfig.FullSync, target) + } + + case <-ticker.C: + if target == nil || !s.exitWhenSynced { + continue + } + if block := s.backend.BlockChain().GetBlockByHash(target.Hash()); block != nil { + log.Info("Sync target reached", "number", block.NumberU64(), "hash", block.Hash()) + go s.stack.Close() // async since we need to close ourselves + return + } + + case <-s.closed: + return + } + } +} + +// Start launches the synchronization service. +func (s *Syncer) Start() error { + s.wg.Add(1) + go s.run() + if s.target == (common.Hash{}) { + return nil + } + return s.Sync(s.target) +} + +// Stop terminates the synchronization service and stop all background activities. +// This function can only be called for one time. +func (s *Syncer) Stop() error { + close(s.closed) + s.wg.Wait() + return nil +} + +// Sync sets the synchronization target. Notably, setting a target lower than the +// previous one is not allowed, as backward synchronization is not supported. +func (s *Syncer) Sync(hash common.Hash) error { + req := &syncReq{ + hash: hash, + errc: make(chan error, 1), + } + select { + case s.request <- req: + return <-req.errc + case <-s.closed: + return errors.New("syncer is closed") + } +} + +// API is the collection of synchronization service APIs for debugging the +// protocol. +type API struct { + s *Syncer +} + +// NewAPI creates a new debug API instance. +func NewAPI(s *Syncer) *API { + return &API{s: s} +} + +// Sync initiates a full sync to the target block hash. +func (api *API) Sync(target common.Hash) error { + return api.s.Sync(target) +} diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index a6d93fc1c5..d7f37a79ee 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -468,6 +468,11 @@ web3._extend({ call: 'debug_getTrieFlushInterval', params: 0 }), + new web3._extend.Method({ + name: 'sync', + call: 'debug_sync', + params: 1 + }), ], properties: [] }); From 32d537cd588efe31e70ad3333cdaaed35f041a21 Mon Sep 17 00:00:00 2001 From: ericxtheodore Date: Mon, 28 Jul 2025 16:13:18 +0800 Subject: [PATCH 42/56] all: replace fmt.Errorf with errors.New (#32286) The errors.new function does not require string formatting, so its performance is better than that of fmt.Errorf. --- cmd/devp2p/internal/ethtest/conn.go | 2 +- cmd/devp2p/internal/ethtest/suite.go | 7 ++++--- cmd/geth/chaincmd.go | 2 +- cmd/workload/testsuite.go | 6 +++--- core/state/snapshot/journal.go | 2 +- core/tracing/journal.go | 6 +++--- core/txpool/validation.go | 2 +- core/types/bal/bal_encoding.go | 2 +- eth/catalyst/api_test.go | 2 +- eth/ethconfig/config.go | 4 ++-- ethdb/leveldb/leveldb.go | 7 ++++--- ethdb/memorydb/memorydb.go | 3 +-- ethdb/pebble/pebble.go | 3 ++- signer/core/apitypes/types.go | 2 +- tests/transaction_test_util.go | 7 ++++--- triedb/pathdb/history_index.go | 2 +- 16 files changed, 31 insertions(+), 28 deletions(-) diff --git a/cmd/devp2p/internal/ethtest/conn.go b/cmd/devp2p/internal/ethtest/conn.go index 4a7a2c76d8..5182d71ce1 100644 --- a/cmd/devp2p/internal/ethtest/conn.go +++ b/cmd/devp2p/internal/ethtest/conn.go @@ -129,7 +129,7 @@ func (c *Conn) Write(proto Proto, code uint64, msg any) error { return err } -var errDisc error = fmt.Errorf("disconnect") +var errDisc error = errors.New("disconnect") // ReadEth reads an Eth sub-protocol wire message. func (c *Conn) ReadEth() (any, error) { diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index b5a346c074..47d00761f3 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -19,6 +19,7 @@ package ethtest import ( "context" "crypto/rand" + "errors" "fmt" "reflect" "sync" @@ -1092,7 +1093,7 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types return } if !readUntilDisconnect(conn) { - errc <- fmt.Errorf("expected bad peer to be disconnected") + errc <- errors.New("expected bad peer to be disconnected") return } stage3.Done() @@ -1139,7 +1140,7 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types } if req.GetPooledTransactionsRequest[0] != tx.Hash() { - errc <- fmt.Errorf("requested unknown tx hash") + errc <- errors.New("requested unknown tx hash") return } @@ -1149,7 +1150,7 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types return } if readUntilDisconnect(conn) { - errc <- fmt.Errorf("unexpected disconnect") + errc <- errors.New("unexpected disconnect") return } close(errc) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 44e11dbf06..112d1a539b 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -716,7 +716,7 @@ func downloadEra(ctx *cli.Context) error { case ctx.IsSet(utils.SepoliaFlag.Name): network = "sepolia" default: - return fmt.Errorf("unsupported network, no known era1 checksums") + return errors.New("unsupported network, no known era1 checksums") } } diff --git a/cmd/workload/testsuite.go b/cmd/workload/testsuite.go index 39eeb8e3c2..25dc17a49e 100644 --- a/cmd/workload/testsuite.go +++ b/cmd/workload/testsuite.go @@ -18,7 +18,7 @@ package main import ( "embed" - "fmt" + "errors" "io/fs" "os" @@ -97,7 +97,7 @@ type testConfig struct { traceTestFile string } -var errPrunedHistory = fmt.Errorf("attempt to access pruned history") +var errPrunedHistory = errors.New("attempt to access pruned history") // validateHistoryPruneErr checks whether the given error is caused by access // to history before the pruning threshold block (it is an rpc.Error with code 4444). @@ -109,7 +109,7 @@ func validateHistoryPruneErr(err error, blockNum uint64, historyPruneBlock *uint if err != nil { if rpcErr, ok := err.(rpc.Error); ok && rpcErr.ErrorCode() == 4444 { if historyPruneBlock != nil && blockNum > *historyPruneBlock { - return fmt.Errorf("pruned history error returned after pruning threshold") + return errors.New("pruned history error returned after pruning threshold") } return errPrunedHistory } diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index e4b396b990..004dd5298a 100644 --- a/core/state/snapshot/journal.go +++ b/core/state/snapshot/journal.go @@ -350,7 +350,7 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error { } if len(destructs) > 0 { log.Warn("Incompatible legacy journal detected", "version", journalV0) - return fmt.Errorf("incompatible legacy journal detected") + return errors.New("incompatible legacy journal detected") } } if err := r.Decode(&accounts); err != nil { diff --git a/core/tracing/journal.go b/core/tracing/journal.go index 8937d4c5ae..a402f1ac09 100644 --- a/core/tracing/journal.go +++ b/core/tracing/journal.go @@ -17,7 +17,7 @@ package tracing import ( - "fmt" + "errors" "math/big" "github.com/ethereum/go-ethereum/common" @@ -39,14 +39,14 @@ type entry interface { // WrapWithJournal wraps the given tracer with a journaling layer. func WrapWithJournal(hooks *Hooks) (*Hooks, error) { if hooks == nil { - return nil, fmt.Errorf("wrapping nil tracer") + return nil, errors.New("wrapping nil tracer") } // No state change to journal, return the wrapped hooks as is if hooks.OnBalanceChange == nil && hooks.OnNonceChange == nil && hooks.OnNonceChangeV2 == nil && hooks.OnCodeChange == nil && hooks.OnStorageChange == nil { return hooks, nil } if hooks.OnNonceChange != nil && hooks.OnNonceChangeV2 != nil { - return nil, fmt.Errorf("cannot have both OnNonceChange and OnNonceChangeV2") + return nil, errors.New("cannot have both OnNonceChange and OnNonceChangeV2") } // Create a new Hooks instance and copy all hooks diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 80ba994d1a..d4f3401086 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -145,7 +145,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types } if tx.Type() == types.SetCodeTxType { if len(tx.SetCodeAuthorizations()) == 0 { - return fmt.Errorf("set code tx must have at least one authorization tuple") + return errors.New("set code tx must have at least one authorization tuple") } } return nil diff --git a/core/types/bal/bal_encoding.go b/core/types/bal/bal_encoding.go index d7d08801b1..24dfafa083 100644 --- a/core/types/bal/bal_encoding.go +++ b/core/types/bal/bal_encoding.go @@ -169,7 +169,7 @@ func (e *AccountAccess) validate() error { // Convert code change if len(e.Code) == 1 { if len(e.Code[0].Code) > params.MaxCodeSize { - return fmt.Errorf("code change contained oversized code") + return errors.New("code change contained oversized code") } } return nil diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index dc7967ba2e..ad377113b5 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -1497,7 +1497,7 @@ func checkEqualBody(a *types.Body, b *engine.ExecutionPayloadBody) error { } } if !reflect.DeepEqual(a.Withdrawals, b.Withdrawals) { - return fmt.Errorf("withdrawals mismatch") + return errors.New("withdrawals mismatch") } return nil } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 97d23744a0..7eba6a6408 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -18,7 +18,7 @@ package ethconfig import ( - "fmt" + "errors" "time" "github.com/ethereum/go-ethereum/common" @@ -171,7 +171,7 @@ type Config struct { func CreateConsensusEngine(config *params.ChainConfig, db ethdb.Database) (consensus.Engine, error) { if config.TerminalTotalDifficulty == nil { log.Error("Geth only supports PoS networks. Please transition legacy networks using Geth v1.13.x.") - return nil, fmt.Errorf("'terminalTotalDifficulty' is not set in genesis block") + return nil, errors.New("'terminalTotalDifficulty' is not set in genesis block") } // Wrap previously supported consensus engines into their post-merge counterpart if config.Clique != nil { diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index 736a44d73d..8e1bb86fec 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -22,6 +22,7 @@ package leveldb import ( "bytes" + "errors" "fmt" "sync" "time" @@ -31,7 +32,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/errors" + lerrors "github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/filter" "github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/util" @@ -120,7 +121,7 @@ func NewCustom(file string, namespace string, customize func(options *opt.Option // Open the db and recover any potential corruptions db, err := leveldb.OpenFile(file, options) - if _, corrupted := err.(*errors.ErrCorrupted); corrupted { + if _, corrupted := err.(*lerrors.ErrCorrupted); corrupted { db, err = leveldb.RecoverFile(file, nil) } if err != nil { @@ -548,7 +549,7 @@ func (r *replayer) DeleteRange(start, end []byte) { if rangeDeleter, ok := r.writer.(ethdb.KeyValueRangeDeleter); ok { r.failure = rangeDeleter.DeleteRange(start, end) } else { - r.failure = fmt.Errorf("ethdb.KeyValueWriter does not implement DeleteRange") + r.failure = errors.New("ethdb.KeyValueWriter does not implement DeleteRange") } } diff --git a/ethdb/memorydb/memorydb.go b/ethdb/memorydb/memorydb.go index 5c4c48de64..200ad60245 100644 --- a/ethdb/memorydb/memorydb.go +++ b/ethdb/memorydb/memorydb.go @@ -20,7 +20,6 @@ package memorydb import ( "bytes" "errors" - "fmt" "sort" "strings" "sync" @@ -327,7 +326,7 @@ func (b *batch) Replay(w ethdb.KeyValueWriter) error { return err } } else { - return fmt.Errorf("ethdb.KeyValueWriter does not implement DeleteRange") + return errors.New("ethdb.KeyValueWriter does not implement DeleteRange") } } continue diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index 58a521f6fb..2370d4654f 100644 --- a/ethdb/pebble/pebble.go +++ b/ethdb/pebble/pebble.go @@ -18,6 +18,7 @@ package pebble import ( + "errors" "fmt" "runtime" "strings" @@ -705,7 +706,7 @@ func (b *batch) Replay(w ethdb.KeyValueWriter) error { return err } } else { - return fmt.Errorf("ethdb.KeyValueWriter does not implement DeleteRange") + return errors.New("ethdb.KeyValueWriter does not implement DeleteRange") } } else { return fmt.Errorf("unhandled operation, keytype: %v", kind) diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 66c750a9c3..b5fd5a2854 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -151,7 +151,7 @@ func (args *SendTxArgs) ToTransaction() (*types.Transaction, error) { al = *args.AccessList } if to == nil { - return nil, fmt.Errorf("transaction recipient must be set for blob transactions") + return nil, errors.New("transaction recipient must be set for blob transactions") } data = &types.BlobTx{ To: *to, diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index b2efabe82e..a90c2d522f 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -17,6 +17,7 @@ package tests import ( + "errors" "fmt" "math/big" @@ -43,7 +44,7 @@ type ttFork struct { func (tt *TransactionTest) validate() error { if tt.Txbytes == nil { - return fmt.Errorf("missing txbytes") + return errors.New("missing txbytes") } for name, fork := range tt.Result { if err := tt.validateFork(fork); err != nil { @@ -58,10 +59,10 @@ func (tt *TransactionTest) validateFork(fork *ttFork) error { return nil } if fork.Hash == nil && fork.Exception == nil { - return fmt.Errorf("missing hash and exception") + return errors.New("missing hash and exception") } if fork.Hash != nil && fork.Sender == nil { - return fmt.Errorf("missing sender") + return errors.New("missing sender") } return nil } diff --git a/triedb/pathdb/history_index.go b/triedb/pathdb/history_index.go index f79581b38b..e781a898e1 100644 --- a/triedb/pathdb/history_index.go +++ b/triedb/pathdb/history_index.go @@ -353,7 +353,7 @@ func (d *indexDeleter) empty() bool { // pop removes the last written element from the index writer. func (d *indexDeleter) pop(id uint64) error { if id == 0 { - return fmt.Errorf("zero history ID is not valid") + return errors.New("zero history ID is not valid") } if id != d.lastID { return fmt.Errorf("pop element out of order, last: %d, this: %d", d.lastID, id) From b64a5001635f7eefe91d9d90dfbf430a3906da7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Andr=C3=B3il?= Date: Mon, 28 Jul 2025 14:56:29 +0200 Subject: [PATCH 43/56] downloader: fix typos, grammar and formatting (#32288) --- eth/downloader/downloader.go | 8 ++++---- eth/downloader/downloader_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index dcda4e521c..09837a3045 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -199,7 +199,7 @@ type BlockChain interface { // InsertChain inserts a batch of blocks into the local chain. InsertChain(types.Blocks) (int, error) - // InterruptInsert whether disables the chain insertion. + // InterruptInsert disables or enables chain insertion. InterruptInsert(on bool) // InsertReceiptChain inserts a batch of blocks along with their receipts @@ -513,7 +513,7 @@ func (d *Downloader) syncToHead() (err error) { // // For non-merged networks, if there is a checkpoint available, then calculate // the ancientLimit through that. Otherwise calculate the ancient limit through - // the advertised height of the remote peer. This most is mostly a fallback for + // the advertised height of the remote peer. This is mostly a fallback for // legacy networks, but should eventually be dropped. TODO(karalabe). // // Beacon sync, use the latest finalized block as the ancient limit @@ -946,7 +946,7 @@ func (d *Downloader) processSnapSyncContent() error { if !d.committed.Load() { latest := results[len(results)-1].Header // If the height is above the pivot block by 2 sets, it means the pivot - // become stale in the network, and it was garbage collected, move to a + // became stale in the network, and it was garbage collected, move to a // new pivot. // // Note, we have `reorgProtHeaderDelay` number of blocks withheld, Those @@ -1043,7 +1043,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state first, last := results[0].Header, results[len(results)-1].Header log.Debug("Inserting snap-sync blocks", "items", len(results), "firstnum", first.Number, "firsthash", first.Hash(), - "lastnumn", last.Number, "lasthash", last.Hash(), + "lastnum", last.Number, "lasthash", last.Hash(), ) blocks := make([]*types.Block, len(results)) receipts := make([]rlp.RawValue, len(results)) diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 669ce003cf..c1a31d6e1c 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -544,7 +544,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) { tester.newPeer("peer 68", eth.ETH68, chain.blocks[1:]) if err := tester.downloader.BeaconSync(mode, chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil { - t.Fatalf("failed to start beacon sync: #{err}") + t.Fatalf("failed to start beacon sync: %v", err) } select { case <-complete: From eb7aef45a73b3151b25a1174d6cddcd0cf2fee0c Mon Sep 17 00:00:00 2001 From: kashitaka Date: Mon, 28 Jul 2025 23:17:36 +0900 Subject: [PATCH 44/56] ethclient/simulated: Fix flaky rollback test (#32280) This PR addresses a flakiness in the rollback test discussed in https://github.com/ethereum/go-ethereum/issues/32252 I found `nonce` collision caused transactions occasionally fail to send. I tried to change error message in the failed test like: ``` if err = client.SendTransaction(ctx, signedTx); err != nil { t.Fatalf("failed to send transaction: %v, nonce: %d", err, signedTx.Nonce()) } ``` and I occasionally got test failure with this message: ``` === CONT TestFlakyFunction/Run_#100 rollback_test.go:44: failed to send transaction: already known, nonce: 0 --- FAIL: TestFlakyFunction/Run_#100 (0.07s) ``` Although `nonces` are obtained via `PendingNonceAt`, we observed that, in rare cases (approximately 1 in 1000), two transactions from the same sender end up with the same nonce. This likely happens because `tx0` has not yet propagated to the transaction pool before `tx1` requests its nonce. When the test succeeds, `tx0` and `tx1` have nonces `0` and `1`, respectively. However, in rare failures, both transactions end up with nonce `0`. We modified the test to explicitly assign nonces to each transaction. By controlling the nonce values manually, we eliminated the race condition and ensured consistent behavior. After several thousand runs, the flakiness was no longer reproducible in my local environment. Reduced internal polling interval in `pendingStateHasTx()` to speed up test execution without impacting stability. It reduces test time for `TestTransactionRollbackBehavior` from about 7 seconds to 2 seconds. --- ethclient/simulated/backend_test.go | 21 +++++++-------------- ethclient/simulated/rollback_test.go | 22 +++++++++++----------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/ethclient/simulated/backend_test.go b/ethclient/simulated/backend_test.go index 7a399d41f3..ee20cd171a 100644 --- a/ethclient/simulated/backend_test.go +++ b/ethclient/simulated/backend_test.go @@ -52,7 +52,7 @@ func simTestBackend(testAddr common.Address) *Backend { ) } -func newBlobTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) { +func newBlobTx(sim *Backend, key *ecdsa.PrivateKey, nonce uint64) (*types.Transaction, error) { client := sim.Client() testBlob := &kzg4844.Blob{0x00} @@ -67,12 +67,8 @@ func newBlobTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) addr := crypto.PubkeyToAddress(key.PublicKey) chainid, _ := client.ChainID(context.Background()) - nonce, err := client.PendingNonceAt(context.Background(), addr) - if err != nil { - return nil, err - } - chainidU256, _ := uint256.FromBig(chainid) + tx := types.NewTx(&types.BlobTx{ ChainID: chainidU256, GasTipCap: gasTipCapU256, @@ -88,7 +84,7 @@ func newBlobTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) return types.SignTx(tx, types.LatestSignerForChainID(chainid), key) } -func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) { +func newTx(sim *Backend, key *ecdsa.PrivateKey, nonce uint64) (*types.Transaction, error) { client := sim.Client() // create a signed transaction to send @@ -96,10 +92,7 @@ func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) { gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei)) addr := crypto.PubkeyToAddress(key.PublicKey) chainid, _ := client.ChainID(context.Background()) - nonce, err := client.PendingNonceAt(context.Background(), addr) - if err != nil { - return nil, err - } + tx := types.NewTx(&types.DynamicFeeTx{ ChainID: chainid, Nonce: nonce, @@ -161,7 +154,7 @@ func TestSendTransaction(t *testing.T) { client := sim.Client() ctx := context.Background() - signedTx, err := newTx(sim, testKey) + signedTx, err := newTx(sim, testKey, 0) if err != nil { t.Errorf("could not create transaction: %v", err) } @@ -252,7 +245,7 @@ func TestForkResendTx(t *testing.T) { parent, _ := client.HeaderByNumber(ctx, nil) // 2. - tx, err := newTx(sim, testKey) + tx, err := newTx(sim, testKey, 0) if err != nil { t.Fatalf("could not create transaction: %v", err) } @@ -297,7 +290,7 @@ func TestCommitReturnValue(t *testing.T) { } // Create a block in the original chain (containing a transaction to force different block hashes) - tx, _ := newTx(sim, testKey) + tx, _ := newTx(sim, testKey, 0) if err := client.SendTransaction(ctx, tx); err != nil { t.Errorf("sending transaction: %v", err) } diff --git a/ethclient/simulated/rollback_test.go b/ethclient/simulated/rollback_test.go index 57c59496d5..093467d291 100644 --- a/ethclient/simulated/rollback_test.go +++ b/ethclient/simulated/rollback_test.go @@ -38,9 +38,9 @@ func TestTransactionRollbackBehavior(t *testing.T) { defer sim.Close() client := sim.Client() - btx0 := testSendSignedTx(t, testKey, sim, true) - tx0 := testSendSignedTx(t, testKey2, sim, false) - tx1 := testSendSignedTx(t, testKey2, sim, false) + btx0 := testSendSignedTx(t, testKey, sim, true, 0) + tx0 := testSendSignedTx(t, testKey2, sim, false, 0) + tx1 := testSendSignedTx(t, testKey2, sim, false, 1) sim.Rollback() @@ -48,9 +48,9 @@ func TestTransactionRollbackBehavior(t *testing.T) { t.Fatalf("all transactions were not rolled back") } - btx2 := testSendSignedTx(t, testKey, sim, true) - tx2 := testSendSignedTx(t, testKey2, sim, false) - tx3 := testSendSignedTx(t, testKey2, sim, false) + btx2 := testSendSignedTx(t, testKey, sim, true, 0) + tx2 := testSendSignedTx(t, testKey2, sim, false, 0) + tx3 := testSendSignedTx(t, testKey2, sim, false, 1) sim.Commit() @@ -61,7 +61,7 @@ func TestTransactionRollbackBehavior(t *testing.T) { // testSendSignedTx sends a signed transaction to the simulated backend. // It does not commit the block. -func testSendSignedTx(t *testing.T, key *ecdsa.PrivateKey, sim *Backend, isBlobTx bool) *types.Transaction { +func testSendSignedTx(t *testing.T, key *ecdsa.PrivateKey, sim *Backend, isBlobTx bool, nonce uint64) *types.Transaction { t.Helper() client := sim.Client() ctx := context.Background() @@ -71,9 +71,9 @@ func testSendSignedTx(t *testing.T, key *ecdsa.PrivateKey, sim *Backend, isBlobT signedTx *types.Transaction ) if isBlobTx { - signedTx, err = newBlobTx(sim, key) + signedTx, err = newBlobTx(sim, key, nonce) } else { - signedTx, err = newTx(sim, key) + signedTx, err = newTx(sim, key, nonce) } if err != nil { t.Fatalf("failed to create transaction: %v", err) @@ -96,13 +96,13 @@ func pendingStateHasTx(client Client, tx *types.Transaction) bool { ) // Poll for receipt with timeout - deadline := time.Now().Add(2 * time.Second) + deadline := time.Now().Add(200 * time.Millisecond) for time.Now().Before(deadline) { receipt, err = client.TransactionReceipt(ctx, tx.Hash()) if err == nil && receipt != nil { break } - time.Sleep(100 * time.Millisecond) + time.Sleep(5 * time.Millisecond) } if err != nil { From a56558d0920b74b6553185de4aff79c3de534e01 Mon Sep 17 00:00:00 2001 From: maskpp Date: Tue, 29 Jul 2025 13:36:30 +0800 Subject: [PATCH 45/56] core/state: preallocate capacity for logs list (#32291) Improvement: preallocate capacity for `logs` at first to avoid reallocating multi times. --- core/state/statedb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index e805885079..7aa6780cfa 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -258,7 +258,7 @@ func (s *StateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common } func (s *StateDB) Logs() []*types.Log { - var logs []*types.Log + logs := make([]*types.Log, 0, s.logSize) for _, lgs := range s.logs { logs = append(logs, lgs...) } From d14d4d2af07090a22f8651366146e3f17e09ed6b Mon Sep 17 00:00:00 2001 From: ericxtheodore Date: Wed, 30 Jul 2025 10:39:03 +0800 Subject: [PATCH 46/56] core/state: improve PrettyPrint function (#32293) --- core/state/access_list.go | 5 +---- core/state/transient_storage.go | 13 ++++--------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/core/state/access_list.go b/core/state/access_list.go index a58c2b20ea..e3f1738864 100644 --- a/core/state/access_list.go +++ b/core/state/access_list.go @@ -145,10 +145,7 @@ func (al *accessList) Equal(other *accessList) bool { // PrettyPrint prints the contents of the access list in a human-readable form func (al *accessList) PrettyPrint() string { out := new(strings.Builder) - var sortedAddrs []common.Address - for addr := range al.addresses { - sortedAddrs = append(sortedAddrs, addr) - } + sortedAddrs := slices.Collect(maps.Keys(al.addresses)) slices.SortFunc(sortedAddrs, common.Address.Cmp) for _, addr := range sortedAddrs { idx := al.addresses[addr] diff --git a/core/state/transient_storage.go b/core/state/transient_storage.go index e63db39eba..3bb4955425 100644 --- a/core/state/transient_storage.go +++ b/core/state/transient_storage.go @@ -18,6 +18,7 @@ package state import ( "fmt" + "maps" "slices" "strings" @@ -70,19 +71,13 @@ func (t transientStorage) Copy() transientStorage { // PrettyPrint prints the contents of the access list in a human-readable form func (t transientStorage) PrettyPrint() string { out := new(strings.Builder) - var sortedAddrs []common.Address - for addr := range t { - sortedAddrs = append(sortedAddrs, addr) - slices.SortFunc(sortedAddrs, common.Address.Cmp) - } + sortedAddrs := slices.Collect(maps.Keys(t)) + slices.SortFunc(sortedAddrs, common.Address.Cmp) for _, addr := range sortedAddrs { fmt.Fprintf(out, "%#x:", addr) - var sortedKeys []common.Hash storage := t[addr] - for key := range storage { - sortedKeys = append(sortedKeys, key) - } + sortedKeys := slices.Collect(maps.Keys(storage)) slices.SortFunc(sortedKeys, common.Hash.Cmp) for _, key := range sortedKeys { fmt.Fprintf(out, " %X : %X\n", key, storage[key]) From 2d95ba7d15879677cb451bbaa5d2c34c2b4f323d Mon Sep 17 00:00:00 2001 From: Daniel Katzan <108216499+dkatzan@users.noreply.github.com> Date: Thu, 31 Jul 2025 08:34:17 +0700 Subject: [PATCH 47/56] core/types: expose sigHash as Hash for SetCodeAuthorization (#32298) --- core/types/tx_setcode.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go index b8e38ef1f7..f2281d4ae7 100644 --- a/core/types/tx_setcode.go +++ b/core/types/tx_setcode.go @@ -89,7 +89,7 @@ type authorizationMarshaling struct { // SignSetCode creates a signed the SetCode authorization. func SignSetCode(prv *ecdsa.PrivateKey, auth SetCodeAuthorization) (SetCodeAuthorization, error) { - sighash := auth.sigHash() + sighash := auth.SigHash() sig, err := crypto.Sign(sighash[:], prv) if err != nil { return SetCodeAuthorization{}, err @@ -105,7 +105,8 @@ func SignSetCode(prv *ecdsa.PrivateKey, auth SetCodeAuthorization) (SetCodeAutho }, nil } -func (a *SetCodeAuthorization) sigHash() common.Hash { +// SigHash returns the hash of SetCodeAuthorization for signing. +func (a *SetCodeAuthorization) SigHash() common.Hash { return prefixedRlpHash(0x05, []any{ a.ChainID, a.Address, @@ -115,7 +116,7 @@ func (a *SetCodeAuthorization) sigHash() common.Hash { // Authority recovers the the authorizing account of an authorization. func (a *SetCodeAuthorization) Authority() (common.Address, error) { - sighash := a.sigHash() + sighash := a.SigHash() if !crypto.ValidateSignatureValues(a.V, a.R.ToBig(), a.S.ToBig(), true) { return common.Address{}, ErrInvalidSig } From 0814d991aba32ce4f2a0253f1c06c2da22788408 Mon Sep 17 00:00:00 2001 From: cui Date: Thu, 31 Jul 2025 09:48:31 +0800 Subject: [PATCH 48/56] common/hexutil: replace customized bit sizer with bit.Uintsize (#32304) --- common/hexutil/hexutil.go | 5 ++--- common/hexutil/json_test.go | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/hexutil/hexutil.go b/common/hexutil/hexutil.go index d3201850a8..d6b6b867f2 100644 --- a/common/hexutil/hexutil.go +++ b/common/hexutil/hexutil.go @@ -34,11 +34,10 @@ import ( "encoding/hex" "fmt" "math/big" + "math/bits" "strconv" ) -const uintBits = 32 << (uint64(^uint(0)) >> 63) - // Errors var ( ErrEmptyString = &decError{"empty hex string"} @@ -48,7 +47,7 @@ var ( ErrEmptyNumber = &decError{"hex string \"0x\""} ErrLeadingZero = &decError{"hex number with leading zero digits"} ErrUint64Range = &decError{"hex number > 64 bits"} - ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)} + ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", bits.UintSize)} ErrBig256Range = &decError{"hex number > 256 bits"} ) diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go index 7cca300951..a014438458 100644 --- a/common/hexutil/json_test.go +++ b/common/hexutil/json_test.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "math/big" + "math/bits" "testing" "github.com/holiman/uint256" @@ -384,7 +385,7 @@ func TestUnmarshalUint(t *testing.T) { for _, test := range unmarshalUintTests { var v Uint err := json.Unmarshal([]byte(test.input), &v) - if uintBits == 32 && test.wantErr32bit != nil { + if bits.UintSize == 32 && test.wantErr32bit != nil { checkError(t, test.input, err, test.wantErr32bit) continue } From 4d9d72806ccd09ce0d452abf77dc77aa3413b972 Mon Sep 17 00:00:00 2001 From: cui Date: Thu, 31 Jul 2025 09:53:31 +0800 Subject: [PATCH 49/56] accounts/abi: precompile regex (#32301) --- accounts/abi/abigen/bind.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/accounts/abi/abigen/bind.go b/accounts/abi/abigen/bind.go index 56e5e214de..08624b04ba 100644 --- a/accounts/abi/abigen/bind.go +++ b/accounts/abi/abigen/bind.go @@ -33,6 +33,10 @@ import ( "github.com/ethereum/go-ethereum/log" ) +var ( + intRegex = regexp.MustCompile(`(u)?int([0-9]*)`) +) + func isKeyWord(arg string) bool { switch arg { case "break": @@ -299,7 +303,7 @@ func bindBasicType(kind abi.Type) string { case abi.AddressTy: return "common.Address" case abi.IntTy, abi.UintTy: - parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) + parts := intRegex.FindStringSubmatch(kind.String()) switch parts[2] { case "8", "16", "32", "64": return fmt.Sprintf("%sint%s", parts[1], parts[2]) From d4a3bf1b23e3972fb82e085c0e29fe2c4647ed5c Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 31 Jul 2025 12:13:36 +0200 Subject: [PATCH 50/56] cmd/devp2p/internal/v4test: add test for ENRRequest (#32303) This adds a cross-client protocol test for a recently discovered bug in Nethermind. --- cmd/devp2p/internal/v4test/discv4tests.go | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/cmd/devp2p/internal/v4test/discv4tests.go b/cmd/devp2p/internal/v4test/discv4tests.go index 963df6cdbc..de97d7a276 100644 --- a/cmd/devp2p/internal/v4test/discv4tests.go +++ b/cmd/devp2p/internal/v4test/discv4tests.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/utesting" "github.com/ethereum/go-ethereum/p2p/discover/v4wire" + "github.com/ethereum/go-ethereum/p2p/enode" ) const ( @@ -501,6 +502,36 @@ func FindnodeAmplificationWrongIP(t *utesting.T) { } } +func ENRRequest(t *utesting.T) { + t.Log(`This test sends an ENRRequest packet and expects a response containing a valid ENR.`) + + te := newTestEnv(Remote, Listen1, Listen2) + defer te.close() + bond(t, te) + + req := &v4wire.ENRRequest{Expiration: futureExpiration()} + hash := te.send(te.l1, req) + + response, _, err := te.read(te.l1) + if err != nil { + t.Fatal("read error:", err) + } + enrResp, ok := response.(*v4wire.ENRResponse) + if !ok { + t.Fatalf("expected ENRResponse packet, got %T", response) + } + if !bytes.Equal(enrResp.ReplyTok, hash) { + t.Errorf("wrong hash in response packet: got %x, want %x", enrResp.ReplyTok, hash) + } + node, err := enode.New(enode.ValidSchemes, &enrResp.Record) + if err != nil { + t.Errorf("invalid record in response: %v", err) + } + if node.ID() != te.remote.ID() { + t.Errorf("wrong node ID in response: got %v, want %v", node.ID(), te.remote.ID()) + } +} + var AllTests = []utesting.Test{ {Name: "Ping/Basic", Fn: BasicPing}, {Name: "Ping/WrongTo", Fn: PingWrongTo}, @@ -510,6 +541,7 @@ var AllTests = []utesting.Test{ {Name: "Ping/PastExpiration", Fn: PingPastExpiration}, {Name: "Ping/WrongPacketType", Fn: WrongPacketType}, {Name: "Ping/BondThenPingWithWrongFrom", Fn: BondThenPingWithWrongFrom}, + {Name: "ENRRequest", Fn: ENRRequest}, {Name: "Findnode/WithoutEndpointProof", Fn: FindnodeWithoutEndpointProof}, {Name: "Findnode/BasicFindnode", Fn: BasicFindnode}, {Name: "Findnode/UnsolicitedNeighbors", Fn: UnsolicitedNeighbors}, From 23da91f73b83eb1d101889b2f773c6f0a80a8f15 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 1 Aug 2025 10:23:23 +0800 Subject: [PATCH 51/56] trie: reduce the memory allocation in trie hashing (#31902) This pull request optimizes trie hashing by reducing memory allocation overhead. Specifically: - define a fullNodeEncoder pool to reuse encoders and avoid memory allocations. - simplify the encoding logic for shortNode and fullNode by getting rid of the Go interfaces. --- trie/hasher.go | 181 ++++++++++++++++++++++++---------------------- trie/iterator.go | 6 +- trie/node.go | 4 - trie/node_enc.go | 23 ++++-- trie/proof.go | 14 +--- trie/trie.go | 6 +- trie/trie_test.go | 1 - 7 files changed, 122 insertions(+), 113 deletions(-) diff --git a/trie/hasher.go b/trie/hasher.go index 16606808c9..a2a1f5b662 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -17,6 +17,8 @@ package trie import ( + "bytes" + "fmt" "sync" "github.com/ethereum/go-ethereum/crypto" @@ -54,7 +56,7 @@ func returnHasherToPool(h *hasher) { } // hash collapses a node down into a hash node. -func (h *hasher) hash(n node, force bool) node { +func (h *hasher) hash(n node, force bool) []byte { // Return the cached hash if it's available if hash, _ := n.cache(); hash != nil { return hash @@ -62,101 +64,110 @@ func (h *hasher) hash(n node, force bool) node { // Trie not processed yet, walk the children switch n := n.(type) { case *shortNode: - collapsed := h.hashShortNodeChildren(n) - hashed := h.shortnodeToHash(collapsed, force) - if hn, ok := hashed.(hashNode); ok { - n.flags.hash = hn - } else { - n.flags.hash = nil + enc := h.encodeShortNode(n) + if len(enc) < 32 && !force { + // Nodes smaller than 32 bytes are embedded directly in their parent. + // In such cases, return the raw encoded blob instead of the node hash. + // It's essential to deep-copy the node blob, as the underlying buffer + // of enc will be reused later. + buf := make([]byte, len(enc)) + copy(buf, enc) + return buf } - return hashed + hash := h.hashData(enc) + n.flags.hash = hash + return hash + case *fullNode: - collapsed := h.hashFullNodeChildren(n) - hashed := h.fullnodeToHash(collapsed, force) - if hn, ok := hashed.(hashNode); ok { - n.flags.hash = hn - } else { - n.flags.hash = nil + enc := h.encodeFullNode(n) + if len(enc) < 32 && !force { + // Nodes smaller than 32 bytes are embedded directly in their parent. + // In such cases, return the raw encoded blob instead of the node hash. + // It's essential to deep-copy the node blob, as the underlying buffer + // of enc will be reused later. + buf := make([]byte, len(enc)) + copy(buf, enc) + return buf } - return hashed - default: - // Value and hash nodes don't have children, so they're left as were + hash := h.hashData(enc) + n.flags.hash = hash + return hash + + case hashNode: + // hash nodes don't have children, so they're left as were return n - } -} -// hashShortNodeChildren returns a copy of the supplied shortNode, with its child -// being replaced by either the hash or an embedded node if the child is small. -func (h *hasher) hashShortNodeChildren(n *shortNode) *shortNode { - var collapsed shortNode - collapsed.Key = hexToCompact(n.Key) - switch n.Val.(type) { - case *fullNode, *shortNode: - collapsed.Val = h.hash(n.Val, false) default: - collapsed.Val = n.Val + panic(fmt.Errorf("unexpected node type, %T", n)) } - return &collapsed } -// hashFullNodeChildren returns a copy of the supplied fullNode, with its child -// being replaced by either the hash or an embedded node if the child is small. -func (h *hasher) hashFullNodeChildren(n *fullNode) *fullNode { - var children [17]node +// encodeShortNode encodes the provided shortNode into the bytes. Notably, the +// return slice must be deep-copied explicitly, otherwise the underlying slice +// will be reused later. +func (h *hasher) encodeShortNode(n *shortNode) []byte { + // Encode leaf node + if hasTerm(n.Key) { + var ln leafNodeEncoder + ln.Key = hexToCompact(n.Key) + ln.Val = n.Val.(valueNode) + ln.encode(h.encbuf) + return h.encodedBytes() + } + // Encode extension node + var en extNodeEncoder + en.Key = hexToCompact(n.Key) + en.Val = h.hash(n.Val, false) + en.encode(h.encbuf) + return h.encodedBytes() +} + +// fnEncoderPool is the pool for storing shared fullNode encoder to mitigate +// the significant memory allocation overhead. +var fnEncoderPool = sync.Pool{ + New: func() interface{} { + var enc fullnodeEncoder + return &enc + }, +} + +// encodeFullNode encodes the provided fullNode into the bytes. Notably, the +// return slice must be deep-copied explicitly, otherwise the underlying slice +// will be reused later. +func (h *hasher) encodeFullNode(n *fullNode) []byte { + fn := fnEncoderPool.Get().(*fullnodeEncoder) + fn.reset() + if h.parallel { var wg sync.WaitGroup for i := 0; i < 16; i++ { - if child := n.Children[i]; child != nil { - wg.Add(1) - go func(i int) { - hasher := newHasher(false) - children[i] = hasher.hash(child, false) - returnHasherToPool(hasher) - wg.Done() - }(i) - } else { - children[i] = nilValueNode + if n.Children[i] == nil { + continue } + wg.Add(1) + go func(i int) { + defer wg.Done() + + h := newHasher(false) + fn.Children[i] = h.hash(n.Children[i], false) + returnHasherToPool(h) + }(i) } wg.Wait() } else { for i := 0; i < 16; i++ { if child := n.Children[i]; child != nil { - children[i] = h.hash(child, false) - } else { - children[i] = nilValueNode + fn.Children[i] = h.hash(child, false) } } } if n.Children[16] != nil { - children[16] = n.Children[16] + fn.Children[16] = n.Children[16].(valueNode) } - return &fullNode{flags: nodeFlag{}, Children: children} -} + fn.encode(h.encbuf) + fnEncoderPool.Put(fn) -// shortNodeToHash computes the hash of the given shortNode. The shortNode must -// first be collapsed, with its key converted to compact form. If the RLP-encoded -// node data is smaller than 32 bytes, the node itself is returned. -func (h *hasher) shortnodeToHash(n *shortNode, force bool) node { - n.encode(h.encbuf) - enc := h.encodedBytes() - - if len(enc) < 32 && !force { - return n // Nodes smaller than 32 bytes are stored inside their parent - } - return h.hashData(enc) -} - -// fullnodeToHash computes the hash of the given fullNode. If the RLP-encoded -// node data is smaller than 32 bytes, the node itself is returned. -func (h *hasher) fullnodeToHash(n *fullNode, force bool) node { - n.encode(h.encbuf) - enc := h.encodedBytes() - - if len(enc) < 32 && !force { - return n // Nodes smaller than 32 bytes are stored inside their parent - } - return h.hashData(enc) + return h.encodedBytes() } // encodedBytes returns the result of the last encoding operation on h.encbuf. @@ -175,9 +186,10 @@ func (h *hasher) encodedBytes() []byte { return h.tmp } -// hashData hashes the provided data -func (h *hasher) hashData(data []byte) hashNode { - n := make(hashNode, 32) +// hashData hashes the provided data. It is safe to modify the returned slice after +// the function returns. +func (h *hasher) hashData(data []byte) []byte { + n := make([]byte, 32) h.sha.Reset() h.sha.Write(data) h.sha.Read(n) @@ -192,20 +204,17 @@ func (h *hasher) hashDataTo(dst, data []byte) { h.sha.Read(dst) } -// proofHash is used to construct trie proofs, and returns the 'collapsed' -// node (for later RLP encoding) as well as the hashed node -- unless the -// node is smaller than 32 bytes, in which case it will be returned as is. -// This method does not do anything on value- or hash-nodes. -func (h *hasher) proofHash(original node) (collapsed, hashed node) { +// proofHash is used to construct trie proofs, returning the rlp-encoded node blobs. +// Note, only resolved node (shortNode or fullNode) is expected for proofing. +// +// It is safe to modify the returned slice after the function returns. +func (h *hasher) proofHash(original node) []byte { switch n := original.(type) { case *shortNode: - sn := h.hashShortNodeChildren(n) - return sn, h.shortnodeToHash(sn, false) + return bytes.Clone(h.encodeShortNode(n)) case *fullNode: - fn := h.hashFullNodeChildren(n) - return fn, h.fullnodeToHash(fn, false) + return bytes.Clone(h.encodeFullNode(n)) default: - // Value and hash nodes don't have children, so they're left as were - return n, n + panic(fmt.Errorf("unexpected node type, %T", original)) } } diff --git a/trie/iterator.go b/trie/iterator.go index fa01611063..e6fedf2430 100644 --- a/trie/iterator.go +++ b/trie/iterator.go @@ -240,9 +240,9 @@ func (it *nodeIterator) LeafProof() [][]byte { for i, item := range it.stack[:len(it.stack)-1] { // Gather nodes that end up as hash nodes (or the root) - node, hashed := hasher.proofHash(item.node) - if _, ok := hashed.(hashNode); ok || i == 0 { - proofs = append(proofs, nodeToBytes(node)) + enc := hasher.proofHash(item.node) + if len(enc) >= 32 || i == 0 { + proofs = append(proofs, enc) } } return proofs diff --git a/trie/node.go b/trie/node.go index 96f077ebbb..74fac4fd4e 100644 --- a/trie/node.go +++ b/trie/node.go @@ -68,10 +68,6 @@ type ( } ) -// nilValueNode is used when collapsing internal trie nodes for hashing, since -// unset children need to serialize correctly. -var nilValueNode = valueNode(nil) - // EncodeRLP encodes a full node into the consensus RLP format. func (n *fullNode) EncodeRLP(w io.Writer) error { eb := rlp.NewEncoderBuffer(w) diff --git a/trie/node_enc.go b/trie/node_enc.go index c95587eeab..02b93ee6f3 100644 --- a/trie/node_enc.go +++ b/trie/node_enc.go @@ -42,18 +42,29 @@ func (n *fullNode) encode(w rlp.EncoderBuffer) { func (n *fullnodeEncoder) encode(w rlp.EncoderBuffer) { offset := w.List() - for _, c := range n.Children { - if c == nil { + for i, c := range n.Children { + if len(c) == 0 { w.Write(rlp.EmptyString) - } else if len(c) < 32 { - w.Write(c) // rawNode } else { - w.WriteBytes(c) // hashNode + // valueNode or hashNode + if i == 16 || len(c) >= 32 { + w.WriteBytes(c) + } else { + w.Write(c) // rawNode + } } } w.ListEnd(offset) } +func (n *fullnodeEncoder) reset() { + for i, c := range n.Children { + if len(c) != 0 { + n.Children[i] = n.Children[i][:0] + } + } +} + func (n *shortNode) encode(w rlp.EncoderBuffer) { offset := w.List() w.WriteBytes(n.Key) @@ -70,7 +81,7 @@ func (n *extNodeEncoder) encode(w rlp.EncoderBuffer) { w.WriteBytes(n.Key) if n.Val == nil { - w.Write(rlp.EmptyString) + w.Write(rlp.EmptyString) // theoretically impossible to happen } else if len(n.Val) < 32 { w.Write(n.Val) // rawNode } else { diff --git a/trie/proof.go b/trie/proof.go index 751d6f620f..53b7acc30c 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -22,6 +22,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" ) @@ -85,16 +86,9 @@ func (t *Trie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error { defer returnHasherToPool(hasher) for i, n := range nodes { - var hn node - n, hn = hasher.proofHash(n) - if hash, ok := hn.(hashNode); ok || i == 0 { - // If the node's database encoding is a hash (or is the - // root node), it becomes a proof element. - enc := nodeToBytes(n) - if !ok { - hash = hasher.hashData(enc) - } - proofDb.Put(hash, enc) + enc := hasher.proofHash(n) + if len(enc) >= 32 || i == 0 { + proofDb.Put(crypto.Keccak256(enc), enc) } } return nil diff --git a/trie/trie.go b/trie/trie.go index fdb4da9be4..222bf8b1f0 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -626,7 +626,7 @@ func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) { // Hash returns the root hash of the trie. It does not write to the // database and can be used even if the trie doesn't have one. func (t *Trie) Hash() common.Hash { - return common.BytesToHash(t.hashRoot().(hashNode)) + return common.BytesToHash(t.hashRoot()) } // Commit collects all dirty nodes in the trie and replaces them with the @@ -677,9 +677,9 @@ func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) { } // hashRoot calculates the root hash of the given trie -func (t *Trie) hashRoot() node { +func (t *Trie) hashRoot() []byte { if t.root == nil { - return hashNode(types.EmptyRootHash.Bytes()) + return types.EmptyRootHash.Bytes() } // If the number of changes is below 100, we let one thread handle it h := newHasher(t.unhashed >= 100) diff --git a/trie/trie_test.go b/trie/trie_test.go index b806ae6b0c..edd85677fe 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -863,7 +863,6 @@ func (s *spongeDb) Flush() { s.sponge.Write([]byte(key)) s.sponge.Write([]byte(s.values[key])) } - fmt.Println(len(s.keys)) } // spongeBatch is a dummy batch which immediately writes to the underlying spongedb From 17d65e9451111288d96f8fb6befb8544a94b6d50 Mon Sep 17 00:00:00 2001 From: lmittmann <3458786+lmittmann@users.noreply.github.com> Date: Fri, 1 Aug 2025 13:57:38 +0200 Subject: [PATCH 52/56] core/vm: add configurable jumpdest analysis cache (#32143) This adds a method on vm.EVM to set the jumpdest cache implementation. It can be used to maintain an analysis cache across VM invocations, to improve performance by skipping the analysis for already known contracts. --------- Co-authored-by: lmittmann Co-authored-by: Felix Lange --- core/vm/analysis_legacy.go | 20 +++++++------- core/vm/analysis_legacy_test.go | 2 +- core/vm/contract.go | 16 +++++------ core/vm/evm.go | 12 ++++++--- core/vm/jumpdests.go | 47 +++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 core/vm/jumpdests.go diff --git a/core/vm/analysis_legacy.go b/core/vm/analysis_legacy.go index 38af9084ac..a445e2048e 100644 --- a/core/vm/analysis_legacy.go +++ b/core/vm/analysis_legacy.go @@ -25,16 +25,16 @@ const ( set7BitsMask = uint16(0b111_1111) ) -// bitvec is a bit vector which maps bytes in a program. +// BitVec is a bit vector which maps bytes in a program. // An unset bit means the byte is an opcode, a set bit means // it's data (i.e. argument of PUSHxx). -type bitvec []byte +type BitVec []byte -func (bits bitvec) set1(pos uint64) { +func (bits BitVec) set1(pos uint64) { bits[pos/8] |= 1 << (pos % 8) } -func (bits bitvec) setN(flag uint16, pos uint64) { +func (bits BitVec) setN(flag uint16, pos uint64) { a := flag << (pos % 8) bits[pos/8] |= byte(a) if b := byte(a >> 8); b != 0 { @@ -42,13 +42,13 @@ func (bits bitvec) setN(flag uint16, pos uint64) { } } -func (bits bitvec) set8(pos uint64) { +func (bits BitVec) set8(pos uint64) { a := byte(0xFF << (pos % 8)) bits[pos/8] |= a bits[pos/8+1] = ^a } -func (bits bitvec) set16(pos uint64) { +func (bits BitVec) set16(pos uint64) { a := byte(0xFF << (pos % 8)) bits[pos/8] |= a bits[pos/8+1] = 0xFF @@ -56,23 +56,23 @@ func (bits bitvec) set16(pos uint64) { } // codeSegment checks if the position is in a code segment. -func (bits *bitvec) codeSegment(pos uint64) bool { +func (bits *BitVec) codeSegment(pos uint64) bool { return (((*bits)[pos/8] >> (pos % 8)) & 1) == 0 } // codeBitmap collects data locations in code. -func codeBitmap(code []byte) bitvec { +func codeBitmap(code []byte) BitVec { // The bitmap is 4 bytes longer than necessary, in case the code // ends with a PUSH32, the algorithm will set bits on the // bitvector outside the bounds of the actual code. - bits := make(bitvec, len(code)/8+1+4) + bits := make(BitVec, len(code)/8+1+4) return codeBitmapInternal(code, bits) } // codeBitmapInternal is the internal implementation of codeBitmap. // It exists for the purpose of being able to run benchmark tests // without dynamic allocations affecting the results. -func codeBitmapInternal(code, bits bitvec) bitvec { +func codeBitmapInternal(code, bits BitVec) BitVec { for pc := uint64(0); pc < uint64(len(code)); { op := OpCode(code[pc]) pc++ diff --git a/core/vm/analysis_legacy_test.go b/core/vm/analysis_legacy_test.go index 471d2b4ffb..f84a4abc92 100644 --- a/core/vm/analysis_legacy_test.go +++ b/core/vm/analysis_legacy_test.go @@ -90,7 +90,7 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) { for i := range code { code[i] = byte(op) } - bits := make(bitvec, len(code)/8+1+4) + bits := make(BitVec, len(code)/8+1+4) b.ResetTimer() for i := 0; i < b.N; i++ { clear(bits) diff --git a/core/vm/contract.go b/core/vm/contract.go index 0eaa91d959..165ca833f8 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -31,8 +31,8 @@ type Contract struct { caller common.Address address common.Address - jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis. - analysis bitvec // Locally cached result of JUMPDEST analysis + jumpDests JumpDestCache // Aggregated result of JUMPDEST analysis. + analysis BitVec // Locally cached result of JUMPDEST analysis Code []byte CodeHash common.Hash @@ -47,15 +47,15 @@ type Contract struct { } // NewContract returns a new contract environment for the execution of EVM. -func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests map[common.Hash]bitvec) *Contract { - // Initialize the jump analysis map if it's nil, mostly for tests +func NewContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests JumpDestCache) *Contract { + // Initialize the jump analysis cache if it's nil, mostly for tests if jumpDests == nil { - jumpDests = make(map[common.Hash]bitvec) + jumpDests = newMapJumpDests() } return &Contract{ caller: caller, address: address, - jumpdests: jumpDests, + jumpDests: jumpDests, Gas: gas, value: value, } @@ -87,12 +87,12 @@ func (c *Contract) isCode(udest uint64) bool { // contracts ( not temporary initcode), we store the analysis in a map if c.CodeHash != (common.Hash{}) { // Does parent context have the analysis? - analysis, exist := c.jumpdests[c.CodeHash] + analysis, exist := c.jumpDests.Load(c.CodeHash) if !exist { // Do the analysis and save in parent context // We do not need to store it in c.analysis analysis = codeBitmap(c.Code) - c.jumpdests[c.CodeHash] = analysis + c.jumpDests.Store(c.CodeHash, analysis) } // Also stash it in current contract for faster access c.analysis = analysis diff --git a/core/vm/evm.go b/core/vm/evm.go index b45a434545..143b7e08a2 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -122,9 +122,8 @@ type EVM struct { // precompiles holds the precompiled contracts for the current epoch precompiles map[common.Address]PrecompiledContract - // jumpDests is the aggregated result of JUMPDEST analysis made through - // the life cycle of EVM. - jumpDests map[common.Hash]bitvec + // jumpDests stores results of JUMPDEST analysis. + jumpDests JumpDestCache } // NewEVM constructs an EVM instance with the supplied block context, state @@ -138,7 +137,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon Config: config, chainConfig: chainConfig, chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time), - jumpDests: make(map[common.Hash]bitvec), + jumpDests: newMapJumpDests(), } evm.precompiles = activePrecompiledContracts(evm.chainRules) evm.interpreter = NewEVMInterpreter(evm) @@ -152,6 +151,11 @@ func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) { evm.precompiles = precompiles } +// SetJumpDestCache configures the analysis cache. +func (evm *EVM) SetJumpDestCache(jumpDests JumpDestCache) { + evm.jumpDests = jumpDests +} + // SetTxContext resets the EVM with a new transaction context. // This is not threadsafe and should only be done very cautiously. func (evm *EVM) SetTxContext(txCtx TxContext) { diff --git a/core/vm/jumpdests.go b/core/vm/jumpdests.go new file mode 100644 index 0000000000..1a30c1943f --- /dev/null +++ b/core/vm/jumpdests.go @@ -0,0 +1,47 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import "github.com/ethereum/go-ethereum/common" + +// JumpDestCache represents the cache of jumpdest analysis results. +type JumpDestCache interface { + // Load retrieves the cached jumpdest analysis for the given code hash. + // Returns the BitVec and true if found, or nil and false if not cached. + Load(codeHash common.Hash) (BitVec, bool) + + // Store saves the jumpdest analysis for the given code hash. + Store(codeHash common.Hash, vec BitVec) +} + +// mapJumpDests is the default implementation of JumpDests using a map. +// This implementation is not thread-safe and is meant to be used per EVM instance. +type mapJumpDests map[common.Hash]BitVec + +// newMapJumpDests creates a new map-based JumpDests implementation. +func newMapJumpDests() JumpDestCache { + return make(mapJumpDests) +} + +func (j mapJumpDests) Load(codeHash common.Hash) (BitVec, bool) { + vec, ok := j[codeHash] + return vec, ok +} + +func (j mapJumpDests) Store(codeHash common.Hash, vec BitVec) { + j[codeHash] = vec +} From 9c58810e717e7c48dff31ebc7280ba47675597a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Andr=C3=B3il?= Date: Fri, 1 Aug 2025 14:00:00 +0200 Subject: [PATCH 53/56] eth: fix typos and outdated comments (#32324) --- eth/ethconfig/config.go | 2 +- eth/filters/filter.go | 1 - eth/filters/filter_system.go | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 7eba6a6408..82c3c500a7 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -154,7 +154,7 @@ type Config struct { // RPCEVMTimeout is the global timeout for eth-call. RPCEVMTimeout time.Duration - // RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for + // RPCTxFeeCap is the global transaction fee (price * gas limit) cap for // send-transaction variants. The unit is ether. RPCTxFeeCap float64 diff --git a/eth/filters/filter.go b/eth/filters/filter.go index ada478ae1d..c4d787eb22 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -456,7 +456,6 @@ func (f *Filter) blockLogs(ctx context.Context, header *types.Header) ([]*types. // checkMatches checks if the receipts belonging to the given header contain any log events that // match the filter criteria. This function is called when the bloom filter signals a potential match. -// skipFilter signals all logs of the given block are requested. func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*types.Log, error) { hash := header.Hash() // Logs in cache are partially filled with context data diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 82c91266c0..751cd417e8 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -207,7 +207,7 @@ type EventSystem struct { } // NewEventSystem creates a new manager that listens for event on the given mux, -// parses and filters them. It uses the all map to retrieve filter changes. The +// parses and filters them. It uses an internal map to retrieve filter changes. The // work loop holds its own index that is used to forward events to filters. // // The returned manager has a loop that needs to be stopped with the Stop function From 038ff766ff9f9d89c1593c75edeb101c0801a0a4 Mon Sep 17 00:00:00 2001 From: Ceyhun Onur Date: Fri, 1 Aug 2025 18:14:30 +0300 Subject: [PATCH 54/56] eth/filters: fix error when blockHash is used with fromBlock/toBlock (#31877) This introduces an error when the filter has both `blockHash` and `fromBlock`/`toBlock`, since these are mutually exclusive. Seems the tests were actually returning `not found` error, which went undetected since there was no check on the actual returned error in the test. --- eth/filters/api.go | 8 ++++ eth/filters/filter.go | 2 +- eth/filters/filter_system_test.go | 67 +++++++++++++++++++++++++------ 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/eth/filters/api.go b/eth/filters/api.go index 232af23957..c929810a12 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -38,6 +38,8 @@ var ( errInvalidTopic = errors.New("invalid topic(s)") errFilterNotFound = errors.New("filter not found") errInvalidBlockRange = errors.New("invalid block range params") + errUnknownBlock = errors.New("unknown block") + errBlockHashWithRange = errors.New("can't specify fromBlock/toBlock with blockHash") errPendingLogsUnsupported = errors.New("pending logs are not supported") errExceedMaxTopics = errors.New("exceed max topics") errExceedMaxAddresses = errors.New("exceed max addresses") @@ -348,8 +350,13 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type if len(crit.Addresses) > maxAddresses { return nil, errExceedMaxAddresses } + var filter *Filter if crit.BlockHash != nil { + if crit.FromBlock != nil || crit.ToBlock != nil { + return nil, errBlockHashWithRange + } + // Block filter requested, construct a single-shot filter filter = api.sys.NewBlockFilter(*crit.BlockHash, crit.Addresses, crit.Topics) } else { @@ -372,6 +379,7 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type // Construct the range filter filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics) } + // Run the filter and return all the logs logs, err := filter.Logs(ctx) if err != nil { diff --git a/eth/filters/filter.go b/eth/filters/filter.go index c4d787eb22..1a9918d0ee 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -85,7 +85,7 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { return nil, err } if header == nil { - return nil, errors.New("unknown block") + return nil, errUnknownBlock } if header.Number.Uint64() < f.sys.backend.HistoryPruningCutoff() { return nil, &history.PrunedHistoryError{} diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 3e686ca2eb..013c1ae527 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -450,24 +450,65 @@ func TestInvalidGetLogsRequest(t *testing.T) { t.Parallel() var ( - db = rawdb.NewMemoryDatabase() - _, sys = newTestFilterSystem(db, Config{}) - api = NewFilterAPI(sys) - blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") + genesis = &core.Genesis{ + Config: params.TestChainConfig, + BaseFee: big.NewInt(params.InitialBaseFee), + } + db, blocks, _ = core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 10, func(i int, gen *core.BlockGen) {}) + _, sys = newTestFilterSystem(db, Config{}) + api = NewFilterAPI(sys) + blockHash = blocks[0].Hash() + unknownBlockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") ) - // Reason: Cannot specify both BlockHash and FromBlock/ToBlock) - testCases := []FilterCriteria{ - 0: {BlockHash: &blockHash, FromBlock: big.NewInt(100)}, - 1: {BlockHash: &blockHash, ToBlock: big.NewInt(500)}, - 2: {BlockHash: &blockHash, FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, - 3: {BlockHash: &blockHash, Topics: [][]common.Hash{{}, {}, {}, {}, {}}}, - 4: {BlockHash: &blockHash, Addresses: make([]common.Address, maxAddresses+1)}, + // Insert the blocks into the chain so filter can look them up + blockchain, err := core.NewBlockChain(db, genesis, ethash.NewFaker(), nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + if n, err := blockchain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + + type testcase struct { + f FilterCriteria + err error + } + testCases := []testcase{ + { + f: FilterCriteria{BlockHash: &blockHash, FromBlock: big.NewInt(100)}, + err: errBlockHashWithRange, + }, + { + f: FilterCriteria{BlockHash: &blockHash, ToBlock: big.NewInt(500)}, + err: errBlockHashWithRange, + }, + { + f: FilterCriteria{BlockHash: &blockHash, FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, + err: errBlockHashWithRange, + }, + { + f: FilterCriteria{BlockHash: &unknownBlockHash}, + err: errUnknownBlock, + }, + { + f: FilterCriteria{BlockHash: &blockHash, Topics: [][]common.Hash{{}, {}, {}, {}, {}}}, + err: errExceedMaxTopics, + }, + { + f: FilterCriteria{BlockHash: &blockHash, Topics: [][]common.Hash{{}, {}, {}, {}, {}}}, + err: errExceedMaxTopics, + }, + { + f: FilterCriteria{BlockHash: &blockHash, Addresses: make([]common.Address, maxAddresses+1)}, + err: errExceedMaxAddresses, + }, } for i, test := range testCases { - if _, err := api.GetLogs(context.Background(), test); err == nil { - t.Errorf("Expected Logs for case #%d to fail", i) + _, err := api.GetLogs(context.Background(), test.f) + if !errors.Is(err, test.err) { + t.Errorf("case %d: wrong error: %q\nwant: %q", i, err, test.err) } } } From 5572f2ed229ff1f3aa0967e32af320a4b01be16d Mon Sep 17 00:00:00 2001 From: VolodymyrBg Date: Fri, 1 Aug 2025 19:30:48 +0300 Subject: [PATCH 55/56] rlp/rlpgen: implement package renaming support (#31148) This adds support for importing types from multiple identically-named packages. --------- Co-authored-by: Felix Lange --- rlp/rlpgen/gen.go | 101 ++++++++++++++++++++------- rlp/rlpgen/gen_test.go | 2 +- rlp/rlpgen/testdata/pkgclash.in.txt | 13 ++++ rlp/rlpgen/testdata/pkgclash.out.txt | 82 ++++++++++++++++++++++ 4 files changed, 173 insertions(+), 25 deletions(-) create mode 100644 rlp/rlpgen/testdata/pkgclash.in.txt create mode 100644 rlp/rlpgen/testdata/pkgclash.out.txt diff --git a/rlp/rlpgen/gen.go b/rlp/rlpgen/gen.go index ff39874737..64841b38a0 100644 --- a/rlp/rlpgen/gen.go +++ b/rlp/rlpgen/gen.go @@ -24,6 +24,7 @@ import ( "sort" "github.com/ethereum/go-ethereum/rlp/internal/rlpstruct" + "golang.org/x/tools/go/packages" ) // buildContext keeps the data needed for make*Op. @@ -96,14 +97,20 @@ func (bctx *buildContext) typeToStructType(typ types.Type) *rlpstruct.Type { // file and assigns unique names of temporary variables. type genContext struct { inPackage *types.Package - imports map[string]struct{} + imports map[string]genImportPackage tempCounter int } +type genImportPackage struct { + alias string + pkg *types.Package +} + func newGenContext(inPackage *types.Package) *genContext { return &genContext{ - inPackage: inPackage, - imports: make(map[string]struct{}), + inPackage: inPackage, + imports: make(map[string]genImportPackage), + tempCounter: 0, } } @@ -117,32 +124,78 @@ func (ctx *genContext) resetTemp() { ctx.tempCounter = 0 } -func (ctx *genContext) addImport(path string) { - if path == ctx.inPackage.Path() { - return // avoid importing the package that we're generating in. +func (ctx *genContext) addImportPath(path string) { + pkg, err := ctx.loadPackage(path) + if err != nil { + panic(fmt.Sprintf("can't load package %q: %v", path, err)) } - // TODO: renaming? - ctx.imports[path] = struct{}{} + ctx.addImport(pkg) } -// importsList returns all packages that need to be imported. -func (ctx *genContext) importsList() []string { - imp := make([]string, 0, len(ctx.imports)) - for k := range ctx.imports { - imp = append(imp, k) +func (ctx *genContext) addImport(pkg *types.Package) string { + if pkg.Path() == ctx.inPackage.Path() { + return "" // avoid importing the package that we're generating in } - sort.Strings(imp) - return imp + if p, exists := ctx.imports[pkg.Path()]; exists { + return p.alias + } + var ( + baseName = pkg.Name() + alias = baseName + counter = 1 + ) + // If the base name conflicts with an existing import, add a numeric suffix. + for ctx.hasAlias(alias) { + alias = fmt.Sprintf("%s%d", baseName, counter) + counter++ + } + ctx.imports[pkg.Path()] = genImportPackage{alias, pkg} + return alias } -// qualify is the types.Qualifier used for printing types. +// hasAlias checks if an alias is already in use +func (ctx *genContext) hasAlias(alias string) bool { + for _, p := range ctx.imports { + if p.alias == alias { + return true + } + } + return false +} + +// loadPackage attempts to load package information +func (ctx *genContext) loadPackage(path string) (*types.Package, error) { + cfg := &packages.Config{Mode: packages.NeedName} + pkgs, err := packages.Load(cfg, path) + if err != nil { + return nil, err + } + if len(pkgs) == 0 { + return nil, fmt.Errorf("no package found for path %s", path) + } + return types.NewPackage(path, pkgs[0].Name), nil +} + +// qualify is the types.Qualifier used for printing types func (ctx *genContext) qualify(pkg *types.Package) string { if pkg.Path() == ctx.inPackage.Path() { return "" } - ctx.addImport(pkg.Path()) - // TODO: renaming? - return pkg.Name() + return ctx.addImport(pkg) +} + +// importsList returns all packages that need to be imported +func (ctx *genContext) importsList() []string { + imp := make([]string, 0, len(ctx.imports)) + for path, p := range ctx.imports { + if p.alias == p.pkg.Name() { + imp = append(imp, fmt.Sprintf("%q", path)) + } else { + imp = append(imp, fmt.Sprintf("%s %q", p.alias, path)) + } + } + sort.Strings(imp) + return imp } type op interface { @@ -359,7 +412,7 @@ func (op uint256Op) genWrite(ctx *genContext, v string) string { } func (op uint256Op) genDecode(ctx *genContext) (string, string) { - ctx.addImport("github.com/holiman/uint256") + ctx.addImportPath("github.com/holiman/uint256") var b bytes.Buffer resultV := ctx.temp() @@ -732,7 +785,7 @@ func (bctx *buildContext) makeOp(name *types.Named, typ types.Type, tags rlpstru // generateDecoder generates the DecodeRLP method on 'typ'. func generateDecoder(ctx *genContext, typ string, op op) []byte { ctx.resetTemp() - ctx.addImport(pathOfPackageRLP) + ctx.addImportPath(pathOfPackageRLP) result, code := op.genDecode(ctx) var b bytes.Buffer @@ -747,8 +800,8 @@ func generateDecoder(ctx *genContext, typ string, op op) []byte { // generateEncoder generates the EncodeRLP method on 'typ'. func generateEncoder(ctx *genContext, typ string, op op) []byte { ctx.resetTemp() - ctx.addImport("io") - ctx.addImport(pathOfPackageRLP) + ctx.addImportPath("io") + ctx.addImportPath(pathOfPackageRLP) var b bytes.Buffer fmt.Fprintf(&b, "func (obj *%s) EncodeRLP(_w io.Writer) error {\n", typ) @@ -783,7 +836,7 @@ func (bctx *buildContext) generate(typ *types.Named, encoder, decoder bool) ([]b var b bytes.Buffer fmt.Fprintf(&b, "package %s\n\n", pkg.Name()) for _, imp := range ctx.importsList() { - fmt.Fprintf(&b, "import %q\n", imp) + fmt.Fprintf(&b, "import %s\n", imp) } if encoder { fmt.Fprintln(&b) diff --git a/rlp/rlpgen/gen_test.go b/rlp/rlpgen/gen_test.go index b4fabb3dc6..4bfb1b9d25 100644 --- a/rlp/rlpgen/gen_test.go +++ b/rlp/rlpgen/gen_test.go @@ -47,7 +47,7 @@ func init() { } } -var tests = []string{"uints", "nil", "rawvalue", "optional", "bigint", "uint256"} +var tests = []string{"uints", "nil", "rawvalue", "optional", "bigint", "uint256", "pkgclash"} func TestOutput(t *testing.T) { for _, test := range tests { diff --git a/rlp/rlpgen/testdata/pkgclash.in.txt b/rlp/rlpgen/testdata/pkgclash.in.txt new file mode 100644 index 0000000000..1d407881ce --- /dev/null +++ b/rlp/rlpgen/testdata/pkgclash.in.txt @@ -0,0 +1,13 @@ +// -*- mode: go -*- + +package test + +import ( + eth1 "github.com/ethereum/go-ethereum/eth" + eth2 "github.com/ethereum/go-ethereum/eth/protocols/eth" +) + +type Test struct { + A eth1.MinerAPI + B eth2.GetReceiptsPacket +} diff --git a/rlp/rlpgen/testdata/pkgclash.out.txt b/rlp/rlpgen/testdata/pkgclash.out.txt new file mode 100644 index 0000000000..d119639b99 --- /dev/null +++ b/rlp/rlpgen/testdata/pkgclash.out.txt @@ -0,0 +1,82 @@ +package test + +import "github.com/ethereum/go-ethereum/common" +import "github.com/ethereum/go-ethereum/eth" +import "github.com/ethereum/go-ethereum/rlp" +import "io" +import eth1 "github.com/ethereum/go-ethereum/eth/protocols/eth" + +func (obj *Test) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + _tmp1 := w.List() + w.ListEnd(_tmp1) + _tmp2 := w.List() + w.WriteUint64(obj.B.RequestId) + _tmp3 := w.List() + for _, _tmp4 := range obj.B.GetReceiptsRequest { + w.WriteBytes(_tmp4[:]) + } + w.ListEnd(_tmp3) + w.ListEnd(_tmp2) + w.ListEnd(_tmp0) + return w.Flush() +} + +func (obj *Test) DecodeRLP(dec *rlp.Stream) error { + var _tmp0 Test + { + if _, err := dec.List(); err != nil { + return err + } + // A: + var _tmp1 eth.MinerAPI + { + if _, err := dec.List(); err != nil { + return err + } + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp0.A = _tmp1 + // B: + var _tmp2 eth1.GetReceiptsPacket + { + if _, err := dec.List(); err != nil { + return err + } + // RequestId: + _tmp3, err := dec.Uint64() + if err != nil { + return err + } + _tmp2.RequestId = _tmp3 + // GetReceiptsRequest: + var _tmp4 []common.Hash + if _, err := dec.List(); err != nil { + return err + } + for dec.MoreDataInList() { + var _tmp5 common.Hash + if err := dec.ReadBytes(_tmp5[:]); err != nil { + return err + } + _tmp4 = append(_tmp4, _tmp5) + } + if err := dec.ListEnd(); err != nil { + return err + } + _tmp2.GetReceiptsRequest = _tmp4 + if err := dec.ListEnd(); err != nil { + return err + } + } + _tmp0.B = _tmp2 + if err := dec.ListEnd(); err != nil { + return err + } + } + *obj = _tmp0 + return nil +} From 5ebd8032b91c305f16c44ab312bde0c373ad5e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felf=C3=B6ldi=20Zsolt?= Date: Mon, 4 Aug 2025 03:19:33 +0200 Subject: [PATCH 56/56] beacon/params, core/filtermaps: update checkpoints (#32336) This PR updates checkpoints for blsync and filtermaps. --- beacon/params/checkpoint_holesky.hex | 2 +- beacon/params/checkpoint_hoodi.hex | 1 + beacon/params/checkpoint_mainnet.hex | 2 +- beacon/params/checkpoint_sepolia.hex | 2 +- beacon/params/networks.go | 5 +++- core/filtermaps/checkpoints_holesky.json | 8 ++++++- core/filtermaps/checkpoints_mainnet.json | 23 ++++++++++++++++++- core/filtermaps/checkpoints_sepolia.json | 29 +++++++++++++++++++++++- 8 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 beacon/params/checkpoint_hoodi.hex diff --git a/beacon/params/checkpoint_holesky.hex b/beacon/params/checkpoint_holesky.hex index 740d7aba21..f4667305b4 100644 --- a/beacon/params/checkpoint_holesky.hex +++ b/beacon/params/checkpoint_holesky.hex @@ -1 +1 @@ -0xd60e5310c5d52ced44cfb13be4e9f22a1e6a6dc56964c3cccd429182d26d72d0 \ No newline at end of file +0x4bae4b97deda095724560012cab1f80a5221ce0a37a4b5236d8ab63f595f29d9 \ No newline at end of file diff --git a/beacon/params/checkpoint_hoodi.hex b/beacon/params/checkpoint_hoodi.hex new file mode 100644 index 0000000000..2885d7c996 --- /dev/null +++ b/beacon/params/checkpoint_hoodi.hex @@ -0,0 +1 @@ +0x1bbf958008172591b6cbdb3d8d52e26998258e83d4bdb9eec10969d84519a6bd \ No newline at end of file diff --git a/beacon/params/checkpoint_mainnet.hex b/beacon/params/checkpoint_mainnet.hex index 45f065ca15..417e69a24b 100644 --- a/beacon/params/checkpoint_mainnet.hex +++ b/beacon/params/checkpoint_mainnet.hex @@ -1 +1 @@ -0x02f0bb348b0d45f95a9b7e2bb5705768ad06548876cee03d880a2c9dabb1ff88 \ No newline at end of file +0x2fe39a39b6f7cbd549e0f74d259de6db486005a65bd3bd92840dd6ce21d6f4c8 \ No newline at end of file diff --git a/beacon/params/checkpoint_sepolia.hex b/beacon/params/checkpoint_sepolia.hex index 3d1b2885b3..02faf72187 100644 --- a/beacon/params/checkpoint_sepolia.hex +++ b/beacon/params/checkpoint_sepolia.hex @@ -1 +1 @@ -0xa0dad451a230c01be6f2492980ec5bb412d8cf33351a75e8b172b5b84a5fd03a \ No newline at end of file +0x86686b2b366e24134e0e3969a9c5f3759f92e5d2b04785b42e22cc7d468c2107 \ No newline at end of file diff --git a/beacon/params/networks.go b/beacon/params/networks.go index 51f67e0c97..b35db34fd6 100644 --- a/beacon/params/networks.go +++ b/beacon/params/networks.go @@ -31,6 +31,9 @@ var checkpointSepolia string //go:embed checkpoint_holesky.hex var checkpointHolesky string +//go:embed checkpoint_hoodi.hex +var checkpointHoodi string + var ( MainnetLightConfig = (&ChainConfig{ GenesisValidatorsRoot: common.HexToHash("0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"), @@ -71,7 +74,7 @@ var ( HoodiLightConfig = (&ChainConfig{ GenesisValidatorsRoot: common.HexToHash("0x212f13fc4df078b6cb7db228f1c8307566dcecf900867401a92023d7ba99cb5f"), GenesisTime: 1742212800, - Checkpoint: common.HexToHash(""), + Checkpoint: common.HexToHash(checkpointHoodi), }). AddFork("GENESIS", 0, common.FromHex("0x10000910")). AddFork("ALTAIR", 0, common.FromHex("0x20000910")). diff --git a/core/filtermaps/checkpoints_holesky.json b/core/filtermaps/checkpoints_holesky.json index a56611cc8e..481c71a114 100644 --- a/core/filtermaps/checkpoints_holesky.json +++ b/core/filtermaps/checkpoints_holesky.json @@ -18,5 +18,11 @@ {"blockNumber": 3221725, "blockId": "0xe771f897dece48b1583cc1d1d10de8015da57407eb1fdf239fdbe46eaab85143", "firstIndex": 1140850137}, {"blockNumber": 3357164, "blockId": "0x6252d0aa54c79623b0680069c88d7b5c47983f0d5c4845b6c811b8d9b5e8ff3c", "firstIndex": 1207959453}, {"blockNumber": 3447019, "blockId": "0xeb7d585e1e063f3cc05ed399fbf6c2df63c271f62f030acb804e9fb1e74b6dc1", "firstIndex": 1275067542}, -{"blockNumber": 3546397, "blockId": "0xdabdef7defa4281180a57c5af121877b82274f15ccf074ea0096146f4c246df2", "firstIndex": 1342176778} +{"blockNumber": 3546397, "blockId": "0xdabdef7defa4281180a57c5af121877b82274f15ccf074ea0096146f4c246df2", "firstIndex": 1342176778}, +{"blockNumber": 3867885, "blockId": "0x8be069dd7a3e2ffb869ee164d11b28555233d2510b134ab9d5484fdae55d2225", "firstIndex": 1409285539}, +{"blockNumber": 3935446, "blockId": "0xc91a61bc215bbcccc3020c62e5c8153162df0d8bcc59813d74671b2d24903ed7", "firstIndex": 1476394742}, +{"blockNumber": 3989508, "blockId": "0xc85dec36a767e44237842ef51915944c2a49780c8c394a3aa6cfb013c99cf58b", "firstIndex": 1543503452}, +{"blockNumber": 4057078, "blockId": "0xccdb79f6705629cb6ab1667a1244938f60911236549143fcff23a3989213e67e", "firstIndex": 1610612030}, +{"blockNumber": 4126499, "blockId": "0x92f2ef21fc911e87e81e38373d5f2915587b9648a0ab3cf4fcfe3e5aaffe7b85", "firstIndex": 1677720416}, +{"blockNumber": 4239335, "blockId": "0x64fbd22965eb583a584552b7edb9b7ce26fb6aad247c1063d0d5a4d11cbcc58c", "firstIndex": 1744830176} ] diff --git a/core/filtermaps/checkpoints_mainnet.json b/core/filtermaps/checkpoints_mainnet.json index 70a08b1aaf..2ea065ddb7 100644 --- a/core/filtermaps/checkpoints_mainnet.json +++ b/core/filtermaps/checkpoints_mainnet.json @@ -267,5 +267,26 @@ {"blockNumber": 22168652, "blockId": "0x6ae43618c915e636794e2cc2d75dde9992766881c7405fe6479c045ed4bee57e", "firstIndex": 17850956277}, {"blockNumber": 22190975, "blockId": "0x9437121647899a4b7b84d67fbea7cc6ff967481c2eab4328ccd86e2cefe19420", "firstIndex": 17918066140}, {"blockNumber": 22234357, "blockId": "0x036030830134f9224160d5a0b62da35ec7813dc8855d554bd22e9d38545243ed", "firstIndex": 17985175075}, -{"blockNumber": 22276736, "blockId": "0x5ceb96d98aa1b4c1c2f2fa253ae9cdb1b04e0420c11bf795400e8762c0a1635c", "firstIndex": 18052284344} +{"blockNumber": 22276736, "blockId": "0x5ceb96d98aa1b4c1c2f2fa253ae9cdb1b04e0420c11bf795400e8762c0a1635c", "firstIndex": 18052284344}, +{"blockNumber": 22321282, "blockId": "0x8a601ebf6a757020c6d43a978f0bd2c150c4acc1ffdd50c7ee88afc78b0c11f8", "firstIndex": 18119392242}, +{"blockNumber": 22349231, "blockId": "0xb751c026a92ba5be95ad7ea4e2729a175b0d0e11a4c108f47cab232b4715d1a2", "firstIndex": 18186501218}, +{"blockNumber": 22377469, "blockId": "0xa47916860a22f7e26761ec2d7f717410791bd3ed0237b2f6266750214c7bbf08", "firstIndex": 18253610249}, +{"blockNumber": 22422685, "blockId": "0x8beaee39603af55fad222730f556c840c41cd76a5eef0bad367ac94d3b86c7aa", "firstIndex": 18320716377}, +{"blockNumber": 22462378, "blockId": "0x6dba9c5d2949f5a6a072267b590e8b15e6fb157a0fc22719387f1fd6bfcd8d5d", "firstIndex": 18387828426}, +{"blockNumber": 22500185, "blockId": "0x2484c380df0a8f7edfdf8d917570d23fab8499aea80c35b6cf4e5fe1e34106e9", "firstIndex": 18454936227}, +{"blockNumber": 22539624, "blockId": "0xd418071906803d25afc3842a6a6468ad3b5fea27107b314ce4e2ccf08b478acf", "firstIndex": 18522044531}, +{"blockNumber": 22577021, "blockId": "0xff222982693f3ff60d2097822171f80a6ddd979080aeb7e995bfb1b973497c84", "firstIndex": 18589154438}, +{"blockNumber": 22614525, "blockId": "0x9868da1fea2ffca3f67e35570f02eb5707b27f6967ea4a109eb4ddbf24566efd", "firstIndex": 18656264174}, +{"blockNumber": 22652848, "blockId": "0x060a911da11ab0f1dda307f5196e622d23901d198925749e70ab58a439477c5a", "firstIndex": 18723372617}, +{"blockNumber": 22692432, "blockId": "0x6a937f2c283aba8c778c1f5ef340b225fd820f8a7dfa6f24f5fe541994f32f2d", "firstIndex": 18790480232}, +{"blockNumber": 22731200, "blockId": "0x00d57a9e7a2dad252436fe9f0382c6a8860d301a9f9ffe6d7ac64c82b95300f8", "firstIndex": 18857590076}, +{"blockNumber": 22769000, "blockId": "0xa48db20307c19c373ef2d31d85088ea14b8df0450491c31982504c87b04edbc0", "firstIndex": 18924699130}, +{"blockNumber": 22808126, "blockId": "0x1419c64ff003edca0586f1c8ec3063da5c54c57ff826cfb34bc866cc18949653", "firstIndex": 18991807807}, +{"blockNumber": 22845231, "blockId": "0x691f87217e61c5d7ae9ad53a44d30e1ab6b1cc3f2b689b9fbf7c38fbacacfe3e", "firstIndex": 19058917062}, +{"blockNumber": 22884189, "blockId": "0x7f102d44c0ea7803f5b0e1a98a6abf0e8383eb99fb114d6f7b4591753ce8bba3", "firstIndex": 19126024122}, +{"blockNumber": 22920923, "blockId": "0x04fe6179495016fc3fe56d8ef5311c360a5761a898262173849c3494fdd73d92", "firstIndex": 19193134595}, +{"blockNumber": 22958100, "blockId": "0xe38e0ff7b0c4065ca42ea577bc32f2566ca46f2ddeedcc4bc1f8fb00e7f26329", "firstIndex": 19260242424}, +{"blockNumber": 22988600, "blockId": "0x04ca74758b22e0ea54b8c992022ff21c16a2af9c45144c3b0f80de921a7eee82", "firstIndex": 19327351273}, +{"blockNumber": 23018392, "blockId": "0x61cc979b00bc97b48356f986a5b9ec997d674bc904c2a2e4b0f17de08e50b3bb", "firstIndex": 19394459627}, +{"blockNumber": 23048524, "blockId": "0x489de15d95739ede4ab15e8b5151d80d4dc85ae10e7be800b1a4723094a678df", "firstIndex": 19461570073} ] diff --git a/core/filtermaps/checkpoints_sepolia.json b/core/filtermaps/checkpoints_sepolia.json index 8d799daefd..234af955e8 100644 --- a/core/filtermaps/checkpoints_sepolia.json +++ b/core/filtermaps/checkpoints_sepolia.json @@ -68,5 +68,32 @@ {"blockNumber": 7911722, "blockId": "0x9a85e48e3135c97c51fc1786f2af0596c802e021b6c53cfca65a129cafcd23ed", "firstIndex": 4496287265}, {"blockNumber": 7960147, "blockId": "0xc9359cc76d7090e1c8a031108f0ab7a8935d971efd4325fe53612a1d99562f6f", "firstIndex": 4563402388}, {"blockNumber": 8030418, "blockId": "0x21867e68cd8327aed2da2601399d60f7f9e41dca4a4f2f9be982e5a2b9304a88", "firstIndex": 4630511616}, -{"blockNumber": 8087701, "blockId": "0x0fa8c8d7549cc9a8d308262706fe248efe759f8b63511efb1e7f3926e9af2dcb", "firstIndex": 4697614758} +{"blockNumber": 8087701, "blockId": "0x0fa8c8d7549cc9a8d308262706fe248efe759f8b63511efb1e7f3926e9af2dcb", "firstIndex": 4697614758}, +{"blockNumber": 8149130, "blockId": "0x655ea638fd9e35cc25f4332f260d7bf98f4f6fa9a72e1bff861209f18659e94c", "firstIndex": 4764727744}, +{"blockNumber": 8208672, "blockId": "0xb5847a670dc3b6181f9e2e40e4218548048366d237a0d12e938b9879bc8cf800", "firstIndex": 4831837882}, +{"blockNumber": 8271345, "blockId": "0x96797214946f29093883b877ccb0f2a9f771a9a3db3794a642b5dcb781c4d194", "firstIndex": 4898942160}, +{"blockNumber": 8302858, "blockId": "0x6a5977b3382ca69a9e0412333f97b911c1f69f857d8f31dd0fc930980e24f2fc", "firstIndex": 4966054626}, +{"blockNumber": 8333618, "blockId": "0x2547294aa23b67c42adbdddfcf424b17a95c4ff0f352a6a2442c529cfb0c892a", "firstIndex": 5033163605}, +{"blockNumber": 8360582, "blockId": "0xf34f5dceb0ef22e0f782b56c12790472acc675997b9c45075bd4e18a9dacd03c", "firstIndex": 5100273631}, +{"blockNumber": 8387230, "blockId": "0x0fbea42e87620b5beeb76b67febc173847c54333d7dce9fa2f8f2a3fa9c8c22a", "firstIndex": 5167381673}, +{"blockNumber": 8414795, "blockId": "0x6c9c000cf5e35da3a7e9e1cd56147c8ce9b43a76d6de945675efd9dc03b628c9", "firstIndex": 5234477010}, +{"blockNumber": 8444749, "blockId": "0xba85f8c9abaddc34e2113eb49385667ba4b008168ae701f46aa7a7ce78c633a1", "firstIndex": 5301598562}, +{"blockNumber": 8474551, "blockId": "0x720866a40242f087dd25b6f0dd79224884f435b114a39e60c5669f5c942c78c1", "firstIndex": 5368707262}, +{"blockNumber": 8501310, "blockId": "0x2b6da233532c701202fb5ac67e005f7d3eb71f88a9fac10c25d24dd11ada05e5", "firstIndex": 5435803858}, +{"blockNumber": 8526970, "blockId": "0x005f9bbad0a10234129d09894d7fcf04bf1398d326510eedb4195808c282802d", "firstIndex": 5502926509}, +{"blockNumber": 8550412, "blockId": "0x37c9f3efc9f33cf62f590087c8c9ac70011883f75e648647a6fd0fec00ca627c", "firstIndex": 5570034950}, +{"blockNumber": 8573540, "blockId": "0x81cfb46a07be7c70bb8a0f76b03a4cd502f92032bea68ad7ba10e26351673000", "firstIndex": 5637137662}, +{"blockNumber": 8590416, "blockId": "0x5c223d58ef22d7b0dd8c498e8498da4787b5dc706681c2bc83849441f5d0922d", "firstIndex": 5704252906}, +{"blockNumber": 8616793, "blockId": "0x9043ce02742fb5ec43a696602867b7ce6003a95b36cd28a37eeb9785a46ad49f", "firstIndex": 5771357264}, +{"blockNumber": 8647290, "blockId": "0xd90115193764b0a33f3f2a719381b3ddbce2532607c72fb287a864eb391eeada", "firstIndex": 5838466144}, +{"blockNumber": 8673192, "blockId": "0x9bc92d340cccaf4c8c03372efc24eb92c5159106729de8d2e9e064f5568d082b", "firstIndex": 5905577457}, +{"blockNumber": 8700694, "blockId": "0xb3d656a173b962bc6825198e94a4974289db06a8998060bd0f5ee2044a7a7deb", "firstIndex": 5972679345}, +{"blockNumber": 8724533, "blockId": "0x253ffc6d77b88fe18736e4c313e9930341c444bc87b2ee22b26cfe8d9d0b178d", "firstIndex": 6039795829}, +{"blockNumber": 8743948, "blockId": "0x04eb66d0261705d31e629193148d0685058d7759ba5f95d2d38e412dbadb8256", "firstIndex": 6106901747}, +{"blockNumber": 8758378, "blockId": "0x64adf54e662d11db716610157da672c3d8b45f001dbce40a269871b86a84d026", "firstIndex": 6174011544}, +{"blockNumber": 8777722, "blockId": "0x0a7f9a956024b404c915e70b42221aa027b2dd715b0697f099dccefae0b9af97", "firstIndex": 6241124215}, +{"blockNumber": 8800154, "blockId": "0x411f90dc18f2bca31fa63615c2866c907bbac1fae8c06782cabfaf788efba665", "firstIndex": 6308233216}, +{"blockNumber": 8829725, "blockId": "0x5686f3a5eec1b070d0113c588f8f4a560d57ad96b8045cedb5c08bbadaa0273e", "firstIndex": 6375340033}, +{"blockNumber": 8858036, "blockId": "0x4f9b5d9fac9c6f6e2224f613cda12e8ab95d636774ce87489dce8a9f805ee2e5", "firstIndex": 6442450330}, +{"blockNumber": 8884811, "blockId": "0x9cf74f978872683802c065e72b5a5326fdad95f19733c34d927b575cd85fd0bd", "firstIndex": 6509559380} ]