Merge remote-tracking branch 'origin/master' into bs/cell-blobpool/sparse-v2

This commit is contained in:
Felix Lange 2026-07-15 13:44:39 +02:00
commit fdce1ff22f
16 changed files with 194 additions and 13 deletions

View file

@ -4,8 +4,10 @@
package {{.Package}} package {{.Package}}
import ( import (
"context"
"math/big" "math/big"
"strings" "strings"
"time"
"errors" "errors"
ethereum "github.com/ethereum/go-ethereum" ethereum "github.com/ethereum/go-ethereum"
@ -27,6 +29,8 @@ var (
_ = types.BloomLookup _ = types.BloomLookup
_ = event.NewSubscription _ = event.NewSubscription
_ = abi.ConvertType _ = abi.ConvertType
_ = time.Tick
_ = context.Background
) )
{{$structs := .Structs}} {{$structs := .Structs}}
@ -77,7 +81,11 @@ var (
return common.Address{}, nil, nil, errors.New("GetABI returned nil") return common.Address{}, nil, nil, errors.New("GetABI returned nil")
} }
{{range $pattern, $name := .Libraries}} {{range $pattern, $name := .Libraries}}
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend) {{decapitalise $name}}Addr, tx, _, _ := Deploy{{capitalise $name}}(auth, backend)
ctx, _ := context.WithTimeout(context.Background(), 5 * time.Second)
if err := bind.WaitAccepted(ctx, backend, tx); err != nil {
return common.Address{}, nil, nil, err
}
{{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:]) {{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
{{end}} {{end}}
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}}) address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})

View file

@ -273,6 +273,12 @@ func (m *MetaData) GetAbi() (*abi.ABI, error) {
// util.go // util.go
// WaitAccepted waits for a tx to be accepted into the pool.
// It stops waiting when the context is canceled.
func WaitAccepted(ctx context.Context, b ContractBackend, tx *types.Transaction) error {
return bind2.WaitAccepted(ctx, b, tx.Hash())
}
// WaitMined waits for tx to be mined on the blockchain. // WaitMined waits for tx to be mined on the blockchain.
// It stops waiting when the context is canceled. // It stops waiting when the context is canceled.
func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) { func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) {

View file

@ -103,6 +103,9 @@ type ContractTransactor interface {
// PendingNonceAt retrieves the current pending nonce associated with an account. // PendingNonceAt retrieves the current pending nonce associated with an account.
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
// TransactionByHash retrieves the transaction associated with the hash, if it exists in the pool.
TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error)
} }
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed. // DeployBackend wraps the operations needed by WaitMined and WaitDeployed.

View file

@ -320,7 +320,7 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
} }
} }
// create the transaction // create the transaction
nonce, err := c.getNonce(opts) nonce, err := c.GetNonce(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -365,7 +365,7 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
} }
} }
// create the transaction // create the transaction
nonce, err := c.getNonce(opts) nonce, err := c.GetNonce(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -402,7 +402,7 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
return c.transactor.EstimateGas(ensureContext(opts.Context), msg) return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
} }
func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) { func (c *BoundContract) GetNonce(opts *TransactOpts) (uint64, error) {
if opts.Nonce == nil { if opts.Nonce == nil {
return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From) return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
} else { } else {

View file

@ -75,6 +75,10 @@ func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transac
return nil return nil
} }
func (mt *mockTransactor) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) {
return nil, false, nil
}
type mockCaller struct { type mockCaller struct {
codeAtBlockNumber *big.Int codeAtBlockNumber *big.Int
callContractBlockNumber *big.Int callContractBlockNumber *big.Int

View file

@ -75,3 +75,28 @@ func WaitDeployed(ctx context.Context, b DeployBackend, hash common.Hash) (commo
} }
return receipt.ContractAddress, err return receipt.ContractAddress, err
} }
func WaitAccepted(ctx context.Context, d ContractBackend, txHash common.Hash) error {
queryTicker := time.NewTicker(time.Second)
defer queryTicker.Stop()
logger := log.New("hash", txHash)
for {
_, _, err := d.TransactionByHash(ctx, txHash)
if err == nil {
return nil
}
if errors.Is(err, ethereum.NotFound) { // TODO: check this is emitted
logger.Trace("Transaction not yet accepted")
} else {
logger.Trace("Transaction submission failed", "err", err)
}
// Wait for the next round.
select {
case <-ctx.Done():
return ctx.Err()
case <-queryTicker.C:
}
}
}

View file

@ -141,6 +141,7 @@ var filterFlags = map[string]nodeFilterC{
"-eth-network": {1, ethFilter}, "-eth-network": {1, ethFilter},
"-les-server": {0, lesFilter}, "-les-server": {0, lesFilter},
"-snap": {0, snapFilter}, "-snap": {0, snapFilter},
"-dialable": {0, dialableFilter},
} }
// parseFilters parses nodeFilters from args. // parseFilters parses nodeFilters from args.
@ -272,3 +273,15 @@ func snapFilter(args []string) (nodeFilter, error) {
} }
return f, nil return f, nil
} }
func dialableFilter(args []string) (nodeFilter, error) {
f := func(n nodeJSON) bool {
var tcp, tcp6, quic, quic6 uint16
n.N.Load((*enr.TCP)(&tcp))
n.N.Load((*enr.TCP6)(&tcp6))
n.N.Load((*enr.QUIC)(&quic))
n.N.Load((*enr.QUIC6)(&quic6))
return tcp != 0 || tcp6 != 0 || quic != 0 || quic6 != 0
}
return f, nil
}

View file

@ -0,0 +1,70 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
)
func TestDialableFilter(t *testing.T) {
filter, err := dialableFilter(nil)
if err != nil {
t.Fatal(err)
}
makeNode := func(entries ...enr.Entry) nodeJSON {
r := new(enr.Record)
for _, e := range entries {
r.Set(e)
}
key, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
if err := enode.SignV4(r, key); err != nil {
t.Fatal(err)
}
n, err := enode.New(enode.ValidSchemes, r)
if err != nil {
t.Fatal(err)
}
return nodeJSON{N: n}
}
tests := []struct {
name string
node nodeJSON
want bool
}{
{"tcp", makeNode(enr.TCP(30303)), true},
{"tcp6", makeNode(enr.TCP6(30303)), true},
{"quic", makeNode(enr.QUIC(30303)), true},
{"no ports", makeNode(), false},
{"udp only", makeNode(enr.UDP(30303)), false},
{"zero tcp", makeNode(enr.TCP(0)), false},
{"oversized tcp", makeNode(enr.WithEntry("tcp", uint32(70000))), false},
}
for _, tt := range tests {
if got := filter(tt.node); got != tt.want {
t.Errorf("%s: dialable = %v, want %v", tt.name, got, tt.want)
}
}
}

View file

@ -102,6 +102,7 @@ var (
utils.CachePreimagesFlag, utils.CachePreimagesFlag,
utils.CacheLogSizeFlag, utils.CacheLogSizeFlag,
utils.FDLimitFlag, utils.FDLimitFlag,
utils.MemoryLimitFlag,
utils.CryptoKZGFlag, utils.CryptoKZGFlag,
utils.ListenPortFlag, utils.ListenPortFlag,
utils.DiscoveryPortFlag, utils.DiscoveryPortFlag,

View file

@ -561,6 +561,11 @@ var (
Usage: "Raise the open file descriptor resource limit (default = system fd limit)", Usage: "Raise the open file descriptor resource limit (default = system fd limit)",
Category: flags.PerfCategory, Category: flags.PerfCategory,
} }
MemoryLimitFlag = &cli.IntFlag{
Name: "memorylimit",
Usage: "Soft memory limit for the Go runtime in megabytes (default = no limit)",
Category: flags.PerfCategory,
}
CryptoKZGFlag = &cli.StringFlag{ CryptoKZGFlag = &cli.StringFlag{
Name: "crypto.kzg", Name: "crypto.kzg",
Usage: "KZG library implementation to use; gokzg (recommended) or ckzg", Usage: "KZG library implementation to use; gokzg (recommended) or ckzg",
@ -1772,12 +1777,13 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
ctx.Set(CacheFlag.Name, strconv.Itoa(allowance)) ctx.Set(CacheFlag.Name, strconv.Itoa(allowance))
} }
} }
// Ensure Go's GC ignores the database cache for trigger percentage
cache := ctx.Int(CacheFlag.Name)
gogc := max(20, min(100, 100/(float64(cache)/1024)))
log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc)) godebug.SetGCPercent(100)
godebug.SetGCPercent(int(gogc)) if ctx.IsSet(MemoryLimitFlag.Name) {
memLimit := int64(ctx.Int(MemoryLimitFlag.Name)) * 1024 * 1024
log.Debug("Setting Go memory limit", "MB", memLimit/1024/1024)
godebug.SetMemoryLimit(memLimit)
}
if ctx.IsSet(SyncTargetFlag.Name) { if ctx.IsSet(SyncTargetFlag.Name) {
cfg.SyncMode = ethconfig.FullSync // dev sync target forces full sync cfg.SyncMode = ethconfig.FullSync // dev sync target forces full sync

View file

@ -112,7 +112,7 @@ func decodeV2(file *os.File) *freezerTableMeta {
return nil return nil
} }
if o.Offset > math.MaxInt64 { if o.Offset > math.MaxInt64 {
log.Error("Invalid flushOffset %d in freezer metadata", "offset", o.Offset, "file", file.Name()) log.Error("Invalid flushOffset in freezer metadata", "offset", o.Offset, "file", file.Name())
return nil return nil
} }
return &freezerTableMeta{ return &freezerTableMeta{

View file

@ -105,7 +105,7 @@ func payloadVersion(config *params.ChainConfig, time uint64) engine.PayloadVersi
switch config.LatestFork(time) { switch config.LatestFork(time) {
case forks.Amsterdam: case forks.Amsterdam:
return engine.PayloadV4 return engine.PayloadV4
case forks.BPO5, forks.BPO4, forks.BPO3, forks.BPO2, forks.BPO1, forks.Osaka, forks.Prague, forks.Cancun: case forks.Bogota, forks.BPO5, forks.BPO4, forks.BPO3, forks.BPO2, forks.BPO1, forks.Osaka, forks.Prague, forks.Cancun:
return engine.PayloadV3 return engine.PayloadV3
case forks.Paris, forks.Shanghai: case forks.Paris, forks.Shanghai:
return engine.PayloadV2 return engine.PayloadV2

View file

@ -258,10 +258,10 @@ func handleMessage(backend Backend, peer *Peer) error {
if err != nil { if err != nil {
return err return err
} }
defer msg.Discard()
if msg.Size > maxMessageSize { if msg.Size > maxMessageSize {
return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize)
} }
defer msg.Discard()
var handlers map[uint64]msgHandler var handlers map[uint64]msgHandler
switch peer.version { switch peer.version {

View file

@ -160,10 +160,10 @@ func HandleMessage(backend Backend, peer *Peer) error {
if err != nil { if err != nil {
return err return err
} }
defer msg.Discard()
if msg.Size > maxMessageSize { if msg.Size > maxMessageSize {
return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize)
} }
defer msg.Discard()
var handlers map[uint64]msgHandler var handlers map[uint64]msgHandler
switch peer.version { switch peer.version {

View file

@ -162,6 +162,9 @@ func MakeTree(seq uint, nodes []*enode.Node, links []string) (*Tree, error) {
if len(n.Record().Signature()) == 0 { if len(n.Record().Signature()) == 0 {
return nil, fmt.Errorf("can't add node %v: unsigned node record", n.ID()) return nil, fmt.Errorf("can't add node %v: unsigned node record", n.ID())
} }
if err := checkRecordPorts(n.Record()); err != nil {
return nil, fmt.Errorf("can't add node %v: %v", n.ID(), err)
}
} }
// Create the leaf list. // Create the leaf list.
@ -393,6 +396,30 @@ func parseENR(e string, validSchemes enr.IdentityScheme) (entry, error) {
return &enrEntry{n}, nil return &enrEntry{n}, nil
} }
var portKeys = []string{
enr.TCP(0).ENRKey(), enr.TCP6(0).ENRKey(),
enr.UDP(0).ENRKey(), enr.UDP6(0).ENRKey(),
enr.QUIC(0).ENRKey(), enr.QUIC6(0).ENRKey(),
}
// checkRecordPorts verifies that port entries, if present, decode as a uint16.
// enr.Record keeps undecodable pairs as raw RLP, so an out-of-range port in a record
// from an external source would otherwise round-trip into a signed tree and fail to
// decode for consumers that read the port strictly.
func checkRecordPorts(r *enr.Record) error {
for _, key := range portKeys {
var port uint16
err := r.Load(enr.WithEntry(key, &port))
if enr.IsNotFound(err) {
continue
}
if err != nil {
return err
}
}
return nil
}
func isValidHash(s string) bool { func isValidHash(s string) bool {
dlen := b32format.DecodedLen(len(s)) dlen := b32format.DecodedLen(len(s))
if dlen < minHashLength || dlen > 32 || strings.ContainsAny(s, "\n\r") { if dlen < minHashLength || dlen > 32 || strings.ContainsAny(s, "\n\r") {

View file

@ -23,6 +23,7 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
) )
func TestParseRoot(t *testing.T) { func TestParseRoot(t *testing.T) {
@ -149,3 +150,20 @@ func TestMakeTree(t *testing.T) {
t.Fatal("too few TXT records in output") t.Fatal("too few TXT records in output")
} }
} }
func TestMakeTreeRejectsUndecodablePort(t *testing.T) {
keys := testKeys(1)
oversized := new(enr.Record)
oversized.Set(enr.WithEntry("udp", uint32(70000)))
if err := enode.SignV4(oversized, keys[0]); err != nil {
t.Fatal(err)
}
n, err := enode.New(enode.ValidSchemes, oversized)
if err != nil {
t.Fatal(err)
}
if _, err := MakeTree(1, []*enode.Node{n}, nil); err == nil {
t.Errorf("MakeTree accepted record with out-of-range port: %s", n.String())
}
}