eth/catalyst: add initial OpenTelemetry tracing for newPayload (#33521)

This PR adds initial OpenTelemetry tracing to the Engine API, focusing
on engine_newPayload*.

```
jsonrpc.engine/newPayloadV4 
|  |- engine.newPayload  [block.number, block.hash, tx.count]
|     |- core.blockchain.InsertBlockWithoutSetHead
|        |- bc.processor.Process
|        |  |- core.ApplyTransactionWithEVM  [tx.hash, tx.index]
|        |  |- core.ApplyTransactionWithEVM  [tx.hash, tx.index]
|        |  |- ...  (one per transaction)
|        |  |- core.postExecution
|        |- bc.validator.ValidateState
```

---------

Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
Jonny Rhea 2026-02-17 10:08:57 -06:00 committed by GitHub
parent 550ca91b17
commit c709c19b40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 162 additions and 82 deletions

View file

@ -20,6 +20,8 @@ require (
github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect
github.com/ferranbt/fastssz v0.1.4 // indirect github.com/ferranbt/fastssz v0.1.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect github.com/gofrs/flock v0.12.1 // indirect
github.com/golang/snappy v1.0.0 // indirect github.com/golang/snappy v1.0.0 // indirect
@ -32,6 +34,10 @@ require (
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.6.1 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel v1.39.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/otel/trace v1.39.0 // indirect
golang.org/x/crypto v0.44.0 // indirect golang.org/x/crypto v0.44.0 // indirect
golang.org/x/sync v0.18.0 // indirect golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.39.0 // indirect golang.org/x/sys v0.39.0 // indirect

View file

@ -48,6 +48,11 @@ github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeD
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
@ -59,6 +64,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
@ -108,6 +117,16 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=

View file

@ -17,6 +17,7 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"runtime/debug" "runtime/debug"
@ -52,7 +53,7 @@ func main() {
} }
vmConfig := vm.Config{} vmConfig := vm.Config{}
crossStateRoot, crossReceiptRoot, err := core.ExecuteStateless(chainConfig, vmConfig, payload.Block, payload.Witness) crossStateRoot, crossReceiptRoot, err := core.ExecuteStateless(context.Background(), chainConfig, vmConfig, payload.Block, payload.Witness)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "stateless self-validation failed: %v\n", err) fmt.Fprintf(os.Stderr, "stateless self-validation failed: %v\n", err)
os.Exit(10) os.Exit(10)

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"context"
"math/big" "math/big"
"testing" "testing"
"time" "time"
@ -210,7 +211,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
t.Fatalf("post-block %d: unexpected result returned: %v", i, result) t.Fatalf("post-block %d: unexpected result returned: %v", i, result)
case <-time.After(25 * time.Millisecond): case <-time.After(25 * time.Millisecond):
} }
chain.InsertBlockWithoutSetHead(postBlocks[i], false) chain.InsertBlockWithoutSetHead(context.Background(), postBlocks[i], false)
} }
// Verify the blocks with pre-merge blocks and post-merge blocks // Verify the blocks with pre-merge blocks and post-merge blocks

View file

@ -18,6 +18,7 @@
package core package core
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -47,6 +48,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/syncx" "github.com/ethereum/go-ethereum/internal/syncx"
"github.com/ethereum/go-ethereum/internal/telemetry"
"github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/internal/version"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
@ -1818,7 +1820,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
} }
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
_, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large) _, n, err := bc.insertChain(context.Background(), chain, true, false) // No witness collection for mass inserts (would get super large)
return n, err return n, err
} }
@ -1830,7 +1832,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// racey behaviour. If a sidechain import is in progress, and the historic state // racey behaviour. If a sidechain import is in progress, and the historic state
// is imported, but then new canon-head is added before the actual sidechain // is imported, but then new canon-head is added before the actual sidechain
// completes, then the historic state could be pruned again // completes, then the historic state could be pruned again
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) { func (bc *BlockChain) insertChain(ctx context.Context, chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) {
// If the chain is terminating, don't even bother starting up. // If the chain is terminating, don't even bother starting up.
if bc.insertStopped() { if bc.insertStopped() {
return nil, 0, nil return nil, 0, nil
@ -1912,11 +1914,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
if setHead { if setHead {
// First block is pruned, insert as sidechain and reorg only if TD grows enough // First block is pruned, insert as sidechain and reorg only if TD grows enough
log.Debug("Pruned ancestor, inserting as sidechain", "number", block.Number(), "hash", block.Hash()) log.Debug("Pruned ancestor, inserting as sidechain", "number", block.Number(), "hash", block.Hash())
return bc.insertSideChain(block, it, makeWitness) return bc.insertSideChain(ctx, block, it, makeWitness)
} else { } else {
// We're post-merge and the parent is pruned, try to recover the parent state // We're post-merge and the parent is pruned, try to recover the parent state
log.Debug("Pruned ancestor", "number", block.Number(), "hash", block.Hash()) log.Debug("Pruned ancestor", "number", block.Number(), "hash", block.Hash())
_, err := bc.recoverAncestors(block, makeWitness) _, err := bc.recoverAncestors(ctx, block, makeWitness)
return nil, it.index, err return nil, it.index, err
} }
// Some other error(except ErrKnownBlock) occurred, abort. // Some other error(except ErrKnownBlock) occurred, abort.
@ -1988,7 +1990,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
} }
// The traced section of block import. // The traced section of block import.
start := time.Now() start := time.Now()
res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1) res, err := bc.ProcessBlock(ctx, parent.Root, block, setHead, makeWitness && len(chain) == 1)
if err != nil { if err != nil {
return nil, it.index, err return nil, it.index, err
} }
@ -2073,7 +2075,7 @@ func (bpr *blockProcessingResult) Stats() *ExecuteStats {
// ProcessBlock executes and validates the given block. If there was no error // ProcessBlock executes and validates the given block. If there was no error
// it writes the block and associated state to database. // it writes the block and associated state to database.
func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (result *blockProcessingResult, blockEndErr error) { func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (result *blockProcessingResult, blockEndErr error) {
var ( var (
err error err error
startTime = time.Now() startTime = time.Now()
@ -2164,7 +2166,9 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
// Process block using the parent state as reference point // Process block using the parent state as reference point
pstart := time.Now() pstart := time.Now()
res, err := bc.processor.Process(block, statedb, bc.cfg.VmConfig) pctx, _, spanEnd := telemetry.StartSpan(ctx, "bc.processor.Process")
res, err := bc.processor.Process(pctx, block, statedb, bc.cfg.VmConfig)
spanEnd(&err)
if err != nil { if err != nil {
bc.reportBadBlock(block, res, err) bc.reportBadBlock(block, res, err)
return nil, err return nil, err
@ -2172,7 +2176,10 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
ptime := time.Since(pstart) ptime := time.Since(pstart)
vstart := time.Now() vstart := time.Now()
if err := bc.validator.ValidateState(block, statedb, res, false); err != nil { _, _, spanEnd = telemetry.StartSpan(ctx, "bc.validator.ValidateState")
err = bc.validator.ValidateState(block, statedb, res, false)
spanEnd(&err)
if err != nil {
bc.reportBadBlock(block, res, err) bc.reportBadBlock(block, res, err)
return nil, err return nil, err
} }
@ -2195,7 +2202,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
task := types.NewBlockWithHeader(context).WithBody(*block.Body()) task := types.NewBlockWithHeader(context).WithBody(*block.Body())
// Run the stateless self-cross-validation // Run the stateless self-cross-validation
crossStateRoot, crossReceiptRoot, err := ExecuteStateless(bc.chainConfig, bc.cfg.VmConfig, task, witness) crossStateRoot, crossReceiptRoot, err := ExecuteStateless(ctx, bc.chainConfig, bc.cfg.VmConfig, task, witness)
if err != nil { if err != nil {
return nil, fmt.Errorf("stateless self-validation failed: %v", err) return nil, fmt.Errorf("stateless self-validation failed: %v", err)
} }
@ -2282,7 +2289,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
// The method writes all (header-and-body-valid) blocks to disk, then tries to // The method writes all (header-and-body-valid) blocks to disk, then tries to
// switch over to the new chain if the TD exceeded the current chain. // switch over to the new chain if the TD exceeded the current chain.
// insertSideChain is only used pre-merge. // insertSideChain is only used pre-merge.
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, makeWitness bool) (*stateless.Witness, int, error) { func (bc *BlockChain) insertSideChain(ctx context.Context, block *types.Block, it *insertIterator, makeWitness bool) (*stateless.Witness, int, error) {
var current = bc.CurrentBlock() var current = bc.CurrentBlock()
// The first sidechain block error is already verified to be ErrPrunedAncestor. // The first sidechain block error is already verified to be ErrPrunedAncestor.
@ -2363,7 +2370,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
// memory here. // memory here.
if len(blocks) >= 2048 || memory > 64*1024*1024 { if len(blocks) >= 2048 || memory > 64*1024*1024 {
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64()) log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
if _, _, err := bc.insertChain(blocks, true, false); err != nil { if _, _, err := bc.insertChain(ctx, blocks, true, false); err != nil {
return nil, 0, err return nil, 0, err
} }
blocks, memory = blocks[:0], 0 blocks, memory = blocks[:0], 0
@ -2377,7 +2384,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
} }
if len(blocks) > 0 { if len(blocks) > 0 {
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64()) log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
return bc.insertChain(blocks, true, makeWitness) return bc.insertChain(ctx, blocks, true, makeWitness)
} }
return nil, 0, nil return nil, 0, nil
} }
@ -2386,7 +2393,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
// all the ancestor blocks since that. // all the ancestor blocks since that.
// recoverAncestors is only used post-merge. // recoverAncestors is only used post-merge.
// We return the hash of the latest block that we could correctly validate. // We return the hash of the latest block that we could correctly validate.
func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (common.Hash, error) { func (bc *BlockChain) recoverAncestors(ctx context.Context, block *types.Block, makeWitness bool) (common.Hash, error) {
// Gather all the sidechain hashes (full blocks may be memory heavy) // Gather all the sidechain hashes (full blocks may be memory heavy)
var ( var (
hashes []common.Hash hashes []common.Hash
@ -2426,7 +2433,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co
} else { } else {
b = bc.GetBlock(hashes[i], numbers[i]) b = bc.GetBlock(hashes[i], numbers[i])
} }
if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0); err != nil { if _, _, err := bc.insertChain(ctx, types.Blocks{b}, false, makeWitness && i == 0); err != nil {
return b.ParentHash(), err return b.ParentHash(), err
} }
} }
@ -2653,14 +2660,16 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
// The key difference between the InsertChain is it won't do the canonical chain // The key difference between the InsertChain is it won't do the canonical chain
// updating. It relies on the additional SetCanonical call to finalize the entire // updating. It relies on the additional SetCanonical call to finalize the entire
// procedure. // procedure.
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, error) { func (bc *BlockChain) InsertBlockWithoutSetHead(ctx context.Context, block *types.Block, makeWitness bool) (witness *stateless.Witness, err error) {
_, _, spanEnd := telemetry.StartSpan(ctx, "core.blockchain.InsertBlockWithoutSetHead")
defer spanEnd(&err)
if !bc.chainmu.TryLock() { if !bc.chainmu.TryLock() {
return nil, errChainStopped return nil, errChainStopped
} }
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness) witness, _, err = bc.insertChain(ctx, types.Blocks{block}, false, makeWitness)
return witness, err return
} }
// SetCanonical rewinds the chain to set the new head block as the specified // SetCanonical rewinds the chain to set the new head block as the specified
@ -2674,7 +2683,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) {
// Re-execute the reorged chain in case the head state is missing. // Re-execute the reorged chain in case the head state is missing.
if !bc.HasState(head.Root()) { if !bc.HasState(head.Root()) {
if latestValidHash, err := bc.recoverAncestors(head, false); err != nil { if latestValidHash, err := bc.recoverAncestors(context.Background(), head, false); err != nil {
return latestValidHash, err return latestValidHash, err
} }
log.Info("Recovered head state", "number", head.Number(), "hash", head.Hash()) log.Info("Recovered head state", "number", head.Number(), "hash", head.Hash())

View file

@ -18,6 +18,7 @@ package core
import ( import (
"bytes" "bytes"
"context"
"errors" "errors"
"fmt" "fmt"
gomath "math" gomath "math"
@ -160,7 +161,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
if err != nil { if err != nil {
return err return err
} }
res, err := blockchain.processor.Process(block, statedb, vm.Config{}) res, err := blockchain.processor.Process(context.Background(), block, statedb, vm.Config{})
if err != nil { if err != nil {
blockchain.reportBadBlock(block, res, err) blockchain.reportBadBlock(block, res, err)
return err return err
@ -3456,7 +3457,7 @@ func testSetCanonical(t *testing.T, scheme string) {
gen.AddTx(tx) gen.AddTx(tx)
}) })
for _, block := range side { for _, block := range side {
_, err := chain.InsertBlockWithoutSetHead(block, false) _, err := chain.InsertBlockWithoutSetHead(context.Background(), block, false)
if err != nil { if err != nil {
t.Fatalf("Failed to insert into chain: %v", err) t.Fatalf("Failed to insert into chain: %v", err)
} }

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"context"
"fmt" "fmt"
"math/big" "math/big"
@ -27,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/telemetry"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
@ -57,7 +59,7 @@ func (p *StateProcessor) chainConfig() *params.ChainConfig {
// Process returns the receipts and logs accumulated during the process and // Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the // returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error. // transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) { func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
var ( var (
config = p.chainConfig() config = p.chainConfig()
receipts types.Receipts receipts types.Receipts
@ -101,30 +103,21 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
} }
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
_, _, spanEnd := telemetry.StartSpan(ctx, "core.ApplyTransactionWithEVM",
telemetry.StringAttribute("tx.hash", tx.Hash().Hex()),
telemetry.Int64Attribute("tx.index", int64(i)),
)
receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, usedGas, evm) receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, usedGas, evm)
spanEnd(&err)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
} }
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...) allLogs = append(allLogs, receipt.Logs...)
} }
// Read requests if Prague is enabled. requests, err := postExecution(ctx, config, block, allLogs, evm)
var requests [][]byte if err != nil {
if config.IsPrague(block.Number(), block.Time()) { return nil, err
requests = [][]byte{}
// EIP-6110
if err := ParseDepositLogs(&requests, allLogs, config); err != nil {
return nil, fmt.Errorf("failed to parse deposit logs: %w", err)
}
// EIP-7002
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
return nil, fmt.Errorf("failed to process withdrawal queue: %w", err)
}
// EIP-7251
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
return nil, fmt.Errorf("failed to process consolidation queue: %w", err)
}
} }
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards) // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
@ -138,6 +131,31 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
}, nil }, nil
} }
// postExecution processes the post-execution system calls if Prague is enabled.
func postExecution(ctx context.Context, config *params.ChainConfig, block *types.Block, allLogs []*types.Log, evm *vm.EVM) (requests [][]byte, err error) {
_, _, spanEnd := telemetry.StartSpan(ctx, "core.postExecution")
defer spanEnd(&err)
// Read requests if Prague is enabled.
if config.IsPrague(block.Number(), block.Time()) {
requests = [][]byte{}
// EIP-6110
if err := ParseDepositLogs(&requests, allLogs, config); err != nil {
return requests, fmt.Errorf("failed to parse deposit logs: %w", err)
}
// EIP-7002
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
return requests, fmt.Errorf("failed to process withdrawal queue: %w", err)
}
// EIP-7251
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
return requests, fmt.Errorf("failed to process consolidation queue: %w", err)
}
}
return requests, nil
}
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database // ApplyTransactionWithEVM attempts to apply a transaction to the given state database
// and uses the input parameters for its environment similar to ApplyTransaction. However, // and uses the input parameters for its environment similar to ApplyTransaction. However,
// this method takes an already created EVM instance as input. // this method takes an already created EVM instance as input.

View file

@ -17,6 +17,8 @@
package core package core
import ( import (
"context"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/beacon"
@ -40,7 +42,7 @@ import (
// - It cannot be placed outside of core, because it needs to construct a dud headerchain // - It cannot be placed outside of core, because it needs to construct a dud headerchain
// //
// TODO(karalabe): Would be nice to resolve both issues above somehow and move it. // TODO(karalabe): Would be nice to resolve both issues above somehow and move it.
func ExecuteStateless(config *params.ChainConfig, vmconfig vm.Config, block *types.Block, witness *stateless.Witness) (common.Hash, common.Hash, error) { func ExecuteStateless(ctx context.Context, config *params.ChainConfig, vmconfig vm.Config, block *types.Block, witness *stateless.Witness) (common.Hash, common.Hash, error) {
// Sanity check if the supplied block accidentally contains a set root or // Sanity check if the supplied block accidentally contains a set root or
// receipt hash. If so, be very loud, but still continue. // receipt hash. If so, be very loud, but still continue.
if block.Root() != (common.Hash{}) { if block.Root() != (common.Hash{}) {
@ -66,7 +68,7 @@ func ExecuteStateless(config *params.ChainConfig, vmconfig vm.Config, block *typ
validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block
// Run the stateless blocks processing and self-validate certain fields // Run the stateless blocks processing and self-validate certain fields
res, err := processor.Process(block, db, vmconfig) res, err := processor.Process(ctx, block, db, vmconfig)
if err != nil { if err != nil {
return common.Hash{}, common.Hash{}, err return common.Hash{}, common.Hash{}, err
} }

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"context"
"sync/atomic" "sync/atomic"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
@ -48,7 +49,7 @@ type Processor interface {
// Process processes the state changes according to the Ethereum rules by running // Process processes the state changes according to the Ethereum rules by running
// the transaction messages using the statedb and applying any rewards to both // the transaction messages using the statedb and applying any rewards to both
// the processor (coinbase) and any included uncles. // the processor (coinbase) and any included uncles.
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error)
} }
// ProcessResult contains the values computed by Process. // ProcessResult contains the values computed by Process.

View file

@ -503,7 +503,7 @@ func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumber) (*stateless.ExtWitness
if parent == nil { if parent == nil {
return &stateless.ExtWitness{}, fmt.Errorf("block number %v found, but parent missing", bn) return &stateless.ExtWitness{}, fmt.Errorf("block number %v found, but parent missing", bn)
} }
result, err := bc.ProcessBlock(parent.Root, block, false, true) result, err := bc.ProcessBlock(context.Background(), parent.Root, block, false, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -520,7 +520,7 @@ func (api *DebugAPI) ExecutionWitnessByHash(hash common.Hash) (*stateless.ExtWit
if parent == nil { if parent == nil {
return &stateless.ExtWitness{}, fmt.Errorf("block number %x found, but parent missing", hash) return &stateless.ExtWitness{}, fmt.Errorf("block number %x found, but parent missing", hash)
} }
result, err := bc.ProcessBlock(parent.Root, block, false, true) result, err := bc.ProcessBlock(context.Background(), parent.Root, block, false, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -18,6 +18,7 @@
package catalyst package catalyst
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
@ -35,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/telemetry"
"github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/internal/version"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
@ -625,15 +627,15 @@ func (api *ConsensusAPI) getBlobs(hashes []common.Hash, v2 bool) ([]*engine.Blob
var invalidStatus = engine.PayloadStatusV1{Status: engine.INVALID} var invalidStatus = engine.PayloadStatusV1{Status: engine.INVALID}
// NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. // NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadV1(ctx context.Context, params engine.ExecutableData) (engine.PayloadStatusV1, error) {
if params.Withdrawals != nil { if params.Withdrawals != nil {
return invalidStatus, paramsErr("withdrawals not supported in V1") return invalidStatus, paramsErr("withdrawals not supported in V1")
} }
return api.newPayload(params, nil, nil, nil, false) return api.newPayload(ctx, params, nil, nil, nil, false)
} }
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. // NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadV2(ctx context.Context, params engine.ExecutableData) (engine.PayloadStatusV1, error) {
var ( var (
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp) cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp) shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
@ -650,11 +652,11 @@ func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.Payl
case params.BlobGasUsed != nil: case params.BlobGasUsed != nil:
return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun") return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun")
} }
return api.newPayload(params, nil, nil, nil, false) return api.newPayload(ctx, params, nil, nil, nil, false)
} }
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. // NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadV3(ctx context.Context, params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
switch { switch {
case params.Withdrawals == nil: case params.Withdrawals == nil:
return invalidStatus, paramsErr("nil withdrawals post-shanghai") return invalidStatus, paramsErr("nil withdrawals post-shanghai")
@ -669,11 +671,11 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas
case !api.checkFork(params.Timestamp, forks.Cancun): case !api.checkFork(params.Timestamp, forks.Cancun):
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
} }
return api.newPayload(params, versionedHashes, beaconRoot, nil, false) return api.newPayload(ctx, params, versionedHashes, beaconRoot, nil, false)
} }
// NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadV4(ctx context.Context, params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
switch { switch {
case params.Withdrawals == nil: case params.Withdrawals == nil:
return invalidStatus, paramsErr("nil withdrawals post-shanghai") return invalidStatus, paramsErr("nil withdrawals post-shanghai")
@ -694,10 +696,10 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas
if err := validateRequests(requests); err != nil { if err := validateRequests(requests); err != nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err)
} }
return api.newPayload(params, versionedHashes, beaconRoot, requests, false) return api.newPayload(ctx, params, versionedHashes, beaconRoot, requests, false)
} }
func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, witness bool) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) newPayload(ctx context.Context, params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, witness bool) (result engine.PayloadStatusV1, err error) {
// The locking here is, strictly, not required. Without these locks, this can happen: // The locking here is, strictly, not required. Without these locks, this can happen:
// //
// 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to // 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to
@ -711,6 +713,13 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
// sequentially. // sequentially.
// Hence, we use a lock here, to be sure that the previous call has finished before we // Hence, we use a lock here, to be sure that the previous call has finished before we
// check whether we already have the block locally. // check whether we already have the block locally.
var attrs = []telemetry.Attribute{
telemetry.Int64Attribute("block.number", int64(params.Number)),
telemetry.StringAttribute("block.hash", params.BlockHash.Hex()),
telemetry.Int64Attribute("tx.count", int64(len(params.Transactions))),
}
ctx, _, spanEnd := telemetry.StartSpan(ctx, "engine.newPayload", attrs...)
defer spanEnd(&err)
api.newPayloadLock.Lock() api.newPayloadLock.Lock()
defer api.newPayloadLock.Unlock() defer api.newPayloadLock.Unlock()
@ -789,7 +798,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
} }
log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number()) log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number())
start := time.Now() start := time.Now()
proofs, err := api.eth.BlockChain().InsertBlockWithoutSetHead(block, witness) proofs, err := api.eth.BlockChain().InsertBlockWithoutSetHead(ctx, block, witness)
processingTime := time.Since(start) processingTime := time.Since(start)
if err != nil { if err != nil {
log.Warn("NewPayload: inserting block failed", "error", err) log.Warn("NewPayload: inserting block failed", "error", err)

View file

@ -314,7 +314,7 @@ func TestEth2NewBlock(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Failed to convert executable data to block %v", err) t.Fatalf("Failed to convert executable data to block %v", err)
} }
newResp, err := api.NewPayloadV1(*execData) newResp, err := api.NewPayloadV1(context.Background(), *execData)
switch { switch {
case err != nil: case err != nil:
t.Fatalf("Failed to insert block: %v", err) t.Fatalf("Failed to insert block: %v", err)
@ -356,7 +356,7 @@ func TestEth2NewBlock(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Failed to convert executable data to block %v", err) t.Fatalf("Failed to convert executable data to block %v", err)
} }
newResp, err := api.NewPayloadV1(*execData) newResp, err := api.NewPayloadV1(context.Background(), *execData)
if err != nil || newResp.Status != "VALID" { if err != nil || newResp.Status != "VALID" {
t.Fatalf("Failed to insert block: %v", err) t.Fatalf("Failed to insert block: %v", err)
} }
@ -502,7 +502,7 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.He
} }
envelope := getNewEnvelope(t, api, parent, w, h) envelope := getNewEnvelope(t, api, parent, w, h)
execResp, err := api.newPayload(*envelope.ExecutionPayload, []common.Hash{}, h, envelope.Requests, false) execResp, err := api.newPayload(context.Background(), *envelope.ExecutionPayload, []common.Hash{}, h, envelope.Requests, false)
if err != nil { if err != nil {
t.Fatalf("can't execute payload: %v", err) t.Fatalf("can't execute payload: %v", err)
} }
@ -648,7 +648,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
t.Fatalf("payload should not be empty") t.Fatalf("payload should not be empty")
} }
} }
execResp, err := api.NewPayloadV1(*payload.ExecutionPayload) execResp, err := api.NewPayloadV1(context.Background(), *payload.ExecutionPayload)
if err != nil { if err != nil {
t.Fatalf("can't execute payload: %v", err) t.Fatalf("can't execute payload: %v", err)
} }
@ -708,7 +708,7 @@ func TestEmptyBlocks(t *testing.T) {
// (1) check LatestValidHash by sending a normal payload (P1'') // (1) check LatestValidHash by sending a normal payload (P1'')
payload := getNewPayload(t, api, commonAncestor, nil, nil) payload := getNewPayload(t, api, commonAncestor, nil, nil)
status, err := api.NewPayloadV1(*payload) status, err := api.NewPayloadV1(context.Background(), *payload)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -724,7 +724,7 @@ func TestEmptyBlocks(t *testing.T) {
payload.GasUsed += 1 payload.GasUsed += 1
payload = setBlockhash(payload) payload = setBlockhash(payload)
// Now latestValidHash should be the common ancestor // Now latestValidHash should be the common ancestor
status, err = api.NewPayloadV1(*payload) status, err = api.NewPayloadV1(context.Background(), *payload)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -742,7 +742,7 @@ func TestEmptyBlocks(t *testing.T) {
payload.ParentHash = common.Hash{1} payload.ParentHash = common.Hash{1}
payload = setBlockhash(payload) payload = setBlockhash(payload)
// Now latestValidHash should be the common ancestor // Now latestValidHash should be the common ancestor
status, err = api.NewPayloadV1(*payload) status, err = api.NewPayloadV1(context.Background(), *payload)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -859,7 +859,7 @@ func TestTrickRemoteBlockCache(t *testing.T) {
// feed the payloads to node B // feed the payloads to node B
for _, payload := range invalidChain { for _, payload := range invalidChain {
status, err := apiB.NewPayloadV1(*payload) status, err := apiB.NewPayloadV1(context.Background(), *payload)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -892,7 +892,7 @@ func TestInvalidBloom(t *testing.T) {
// (1) check LatestValidHash by sending a normal payload (P1'') // (1) check LatestValidHash by sending a normal payload (P1'')
payload := getNewPayload(t, api, commonAncestor, nil, nil) payload := getNewPayload(t, api, commonAncestor, nil, nil)
payload.LogsBloom = append(payload.LogsBloom, byte(1)) payload.LogsBloom = append(payload.LogsBloom, byte(1))
status, err := api.NewPayloadV1(*payload) status, err := api.NewPayloadV1(context.Background(), *payload)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -931,7 +931,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
for ii := 0; ii < 10; ii++ { for ii := 0; ii < 10; ii++ {
go func() { go func() {
defer wg.Done() defer wg.Done()
if newResp, err := api.NewPayloadV1(*execData); err != nil { if newResp, err := api.NewPayloadV1(context.Background(), *execData); err != nil {
errMu.Lock() errMu.Lock()
testErr = fmt.Errorf("failed to insert block: %w", err) testErr = fmt.Errorf("failed to insert block: %w", err)
errMu.Unlock() errMu.Unlock()
@ -1038,7 +1038,7 @@ func TestWithdrawals(t *testing.T) {
} }
// 10: verify locally built block // 10: verify locally built block
if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil { if status, err := api.NewPayloadV2(context.Background(), *execData.ExecutionPayload); err != nil {
t.Fatalf("error validating payload: %v", err) t.Fatalf("error validating payload: %v", err)
} else if status.Status != engine.VALID { } else if status.Status != engine.VALID {
t.Fatalf("invalid payload") t.Fatalf("invalid payload")
@ -1082,7 +1082,7 @@ func TestWithdrawals(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("error getting payload, err=%v", err) t.Fatalf("error getting payload, err=%v", err)
} }
if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil { if status, err := api.NewPayloadV2(context.Background(), *execData.ExecutionPayload); err != nil {
t.Fatalf("error validating payload: %v", err) t.Fatalf("error validating payload: %v", err)
} else if status.Status != engine.VALID { } else if status.Status != engine.VALID {
t.Fatalf("invalid payload") t.Fatalf("invalid payload")
@ -1225,9 +1225,9 @@ func TestNilWithdrawals(t *testing.T) {
} }
var status engine.PayloadStatusV1 var status engine.PayloadStatusV1
if !shanghai { if !shanghai {
status, err = api.NewPayloadV1(*execData.ExecutionPayload) status, err = api.NewPayloadV1(context.Background(), *execData.ExecutionPayload)
} else { } else {
status, err = api.NewPayloadV2(*execData.ExecutionPayload) status, err = api.NewPayloadV2(context.Background(), *execData.ExecutionPayload)
} }
if err != nil { if err != nil {
t.Fatalf("error validating payload: %v", err.(*engine.EngineAPIError).ErrorData()) t.Fatalf("error validating payload: %v", err.(*engine.EngineAPIError).ErrorData())
@ -1598,7 +1598,7 @@ func TestParentBeaconBlockRoot(t *testing.T) {
} }
// 11: verify locally built block // 11: verify locally built block
if status, err := api.NewPayloadV3(*execData.ExecutionPayload, []common.Hash{}, &common.Hash{42}); err != nil { if status, err := api.NewPayloadV3(context.Background(), *execData.ExecutionPayload, []common.Hash{}, &common.Hash{42}); err != nil {
t.Fatalf("error validating payload: %v", err) t.Fatalf("error validating payload: %v", err)
} else if status.Status != engine.VALID { } else if status.Status != engine.VALID {
t.Fatalf("invalid payload") t.Fatalf("invalid payload")
@ -1705,7 +1705,7 @@ func TestWitnessCreationAndConsumption(t *testing.T) {
envelope.ExecutionPayload.StateRoot = wantStateRoot envelope.ExecutionPayload.StateRoot = wantStateRoot
envelope.ExecutionPayload.ReceiptsRoot = wantReceiptRoot envelope.ExecutionPayload.ReceiptsRoot = wantReceiptRoot
res2, err := api.NewPayloadWithWitnessV3(*envelope.ExecutionPayload, []common.Hash{}, &common.Hash{42}) res2, err := api.NewPayloadWithWitnessV3(context.Background(), *envelope.ExecutionPayload, []common.Hash{}, &common.Hash{42})
if err != nil { if err != nil {
t.Fatalf("error executing stateless payload witness: %v", err) t.Fatalf("error executing stateless payload witness: %v", err)
} }

View file

@ -17,6 +17,7 @@
package catalyst package catalyst
import ( import (
"context"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"errors" "errors"
@ -32,11 +33,13 @@ import (
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/telemetry"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/params/forks" "github.com/ethereum/go-ethereum/params/forks"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"go.opentelemetry.io/otel"
) )
const devEpochLength = 32 const devEpochLength = 32
@ -191,6 +194,7 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
} }
version := payloadVersion(c.eth.BlockChain().Config(), timestamp) version := payloadVersion(c.eth.BlockChain().Config(), timestamp)
tracer := otel.Tracer("")
var random [32]byte var random [32]byte
rand.Read(random[:]) rand.Read(random[:])
@ -255,8 +259,16 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
requests = envelope.Requests requests = envelope.Requests
} }
// Create a server span for newPayload, simulating the consensus client
// sending the execution payload for validation.
npCtx, npSpanEnd := telemetry.StartServerSpan(context.Background(), tracer, telemetry.RPCInfo{
System: "jsonrpc",
Service: "engine",
Method: "newPayloadV" + fmt.Sprintf("%d", version),
})
// Mark the payload as canon // Mark the payload as canon
_, err = c.engineAPI.newPayload(*payload, blobHashes, beaconRoot, requests, false) _, err = c.engineAPI.newPayload(npCtx, *payload, blobHashes, beaconRoot, requests, false)
npSpanEnd(&err)
if err != nil { if err != nil {
return err return err
} }

View file

@ -17,6 +17,7 @@
package catalyst package catalyst
import ( import (
"context"
"errors" "errors"
"strconv" "strconv"
"time" "time"
@ -86,16 +87,16 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3(update engine.Forkchoice
// NewPayloadWithWitnessV1 is analogous to NewPayloadV1, only it also generates // NewPayloadWithWitnessV1 is analogous to NewPayloadV1, only it also generates
// and returns a stateless witness after running the payload. // and returns a stateless witness after running the payload.
func (api *ConsensusAPI) NewPayloadWithWitnessV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadWithWitnessV1(ctx context.Context, params engine.ExecutableData) (engine.PayloadStatusV1, error) {
if params.Withdrawals != nil { if params.Withdrawals != nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1")) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1"))
} }
return api.newPayload(params, nil, nil, nil, true) return api.newPayload(ctx, params, nil, nil, nil, true)
} }
// NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates // NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates
// and returns a stateless witness after running the payload. // and returns a stateless witness after running the payload.
func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadWithWitnessV2(ctx context.Context, params engine.ExecutableData) (engine.PayloadStatusV1, error) {
var ( var (
cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp) cancun = api.config().IsCancun(api.config().LondonBlock, params.Timestamp)
shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp) shanghai = api.config().IsShanghai(api.config().LondonBlock, params.Timestamp)
@ -112,12 +113,12 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) (
case params.BlobGasUsed != nil: case params.BlobGasUsed != nil:
return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun") return invalidStatus, paramsErr("non-nil blobGasUsed pre-cancun")
} }
return api.newPayload(params, nil, nil, nil, true) return api.newPayload(ctx, params, nil, nil, nil, true)
} }
// NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates // NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates
// and returns a stateless witness after running the payload. // and returns a stateless witness after running the payload.
func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadWithWitnessV3(ctx context.Context, params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
switch { switch {
case params.Withdrawals == nil: case params.Withdrawals == nil:
return invalidStatus, paramsErr("nil withdrawals post-shanghai") return invalidStatus, paramsErr("nil withdrawals post-shanghai")
@ -132,12 +133,12 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v
case !api.checkFork(params.Timestamp, forks.Cancun): case !api.checkFork(params.Timestamp, forks.Cancun):
return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads") return invalidStatus, unsupportedForkErr("newPayloadV3 must only be called for cancun payloads")
} }
return api.newPayload(params, versionedHashes, beaconRoot, nil, true) return api.newPayload(ctx, params, versionedHashes, beaconRoot, nil, true)
} }
// NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates // NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates
// and returns a stateless witness after running the payload. // and returns a stateless witness after running the payload.
func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadWithWitnessV4(ctx context.Context, params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) {
switch { switch {
case params.Withdrawals == nil: case params.Withdrawals == nil:
return invalidStatus, paramsErr("nil withdrawals post-shanghai") return invalidStatus, paramsErr("nil withdrawals post-shanghai")
@ -158,7 +159,7 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, v
if err := validateRequests(requests); err != nil { if err := validateRequests(requests); err != nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(err)
} }
return api.newPayload(params, versionedHashes, beaconRoot, requests, true) return api.newPayload(ctx, params, versionedHashes, beaconRoot, requests, true)
} }
// ExecuteStatelessPayloadV1 is analogous to NewPayloadV1, only it operates in // ExecuteStatelessPayloadV1 is analogous to NewPayloadV1, only it operates in
@ -283,7 +284,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v
api.lastNewPayloadUpdate.Store(time.Now().Unix()) api.lastNewPayloadUpdate.Store(time.Now().Unix())
log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash) log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash)
stateRoot, receiptRoot, err := core.ExecuteStateless(api.config(), vm.Config{}, block, witness) stateRoot, receiptRoot, err := core.ExecuteStateless(context.Background(), api.config(), vm.Config{}, block, witness)
if err != nil { if err != nil {
log.Warn("ExecuteStatelessPayload: execution failed", "err", err) log.Warn("ExecuteStatelessPayload: execution failed", "err", err)
errorMsg := err.Error() errorMsg := err.Error()

View file

@ -147,7 +147,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
if current = eth.blockchain.GetBlockByNumber(next); current == nil { if current = eth.blockchain.GetBlockByNumber(next); current == nil {
return nil, nil, fmt.Errorf("block #%d not found", next) return nil, nil, fmt.Errorf("block #%d not found", next)
} }
_, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) _, err := eth.blockchain.Processor().Process(ctx, current, statedb, vm.Config{})
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) return nil, nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
} }