ai implementation for preimages

This commit is contained in:
Jared Wasinger 2026-06-17 14:50:45 -04:00
parent 861890877a
commit d9f607e867
3 changed files with 148 additions and 4 deletions

View file

@ -8,6 +8,7 @@ import (
"slices" "slices"
"time" "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types/bal" "github.com/ethereum/go-ethereum/core/types/bal"
@ -22,6 +23,12 @@ type ProcessResultWithMetrics struct {
StateTransitionMetrics *state.BALStateTransitionMetrics StateTransitionMetrics *state.BALStateTransitionMetrics
ExecTime time.Duration ExecTime time.Duration
PostProcessTime time.Duration PostProcessTime time.Duration
// Preimages holds the SHA3 preimages seen by the VM across the whole block
// (the pre/post-transaction system executions and every transaction). They
// are gathered here so the caller can install them into the state
// transition without the execution helpers needing a reference to it.
Preimages map[common.Hash][]byte
} }
// errResult wraps an error into a new ProcessResultWithMetrics instance // errResult wraps an error into a new ProcessResultWithMetrics instance
@ -112,6 +119,24 @@ func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, tExecStar
p.chain.Engine().Finalize(p.chain, block.Header(), evm.StateDB, block.Body(), uint32(lastBALIdx), postBAL) p.chain.Engine().Finalize(p.chain, block.Header(), evm.StateDB, block.Body(), uint32(lastBALIdx), postBAL)
// Gather the SHA3 preimages seen across the block. postTxState is a copy of
// the block statedb taken after pre-transaction processing, so its set
// already covers both the pre-tx system execution and the post-tx execution
// (PostExecution + Finalize) performed above; the per-transaction sets are
// carried on each result. The merged set is returned to the caller, which
// owns the state transition and installs them there.
preimages := make(map[common.Hash][]byte)
for hash, preimage := range postTxState.Preimages() {
preimages[hash] = preimage
}
for i := range results {
for hash, preimage := range results[i].preimages {
if _, ok := preimages[hash]; !ok {
preimages[hash] = preimage
}
}
}
blockAccessList := bal.NewConstructionBlockAccessList() blockAccessList := bal.NewConstructionBlockAccessList()
blockAccessList.Merge(preTxBAL) blockAccessList.Merge(preTxBAL)
blockAccessList.Merge(postBAL) blockAccessList.Merge(postBAL)
@ -137,6 +162,7 @@ func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, tExecStar
}, },
PostProcessTime: tPostprocess, PostProcessTime: tPostprocess,
ExecTime: tExec, ExecTime: tExec,
Preimages: preimages,
} }
} }
@ -150,6 +176,10 @@ type txExecResult struct {
txState uint64 txState uint64
blockAccessList *bal.ConstructionBlockAccessList blockAccessList *bal.ConstructionBlockAccessList
// preimages holds the SHA3 preimages recorded while executing this
// transaction against its own statedb copy.
preimages map[common.Hash][]byte
} }
// resultHandler polls until all transactions have finished executing and the // resultHandler polls until all transactions have finished executing and the
@ -303,7 +333,11 @@ func (p *ParallelStateProcessor) Process(block *types.Block, stateTransition *st
prestate := startingState.Copy() prestate := startingState.Copy()
workers.Go(func() error { workers.Go(func() error {
prestate = prestate.WithReader(state.NewReaderWithAccessList(statedb.Reader(), prepared, balIdx)) prestate = prestate.WithReader(state.NewReaderWithAccessList(statedb.Reader(), prepared, balIdx))
txResCh <- *p.execTx(block, tx, balIdx, prestate, signer) res := p.execTx(block, tx, balIdx, prestate, signer)
// Carry this transaction's preimages on the result; they're merged
// into the block-level set in prepareExecResult.
res.preimages = prestate.Preimages()
txResCh <- *res
return nil return nil
}) })
} }
@ -314,6 +348,10 @@ func (p *ParallelStateProcessor) Process(block *types.Block, stateTransition *st
if res.ProcessResult.Error != nil { if res.ProcessResult.Error != nil {
return nil, res.ProcessResult.Error return nil, res.ProcessResult.Error
} }
// Install the block's preimages into the state transition so they're
// persisted when the block is committed. Process is the sole owner of the
// state transition, keeping the execution helpers free of it.
stateTransition.AddPreimages(res.Preimages)
// TODO: remove preprocess metric ? // TODO: remove preprocess metric ?
res.PreProcessTime = tPreprocess res.PreProcessTime = tPreprocess
return res, nil return res, nil

View file

@ -47,6 +47,12 @@ type BALStateTransition struct {
stateUpdate *StateUpdate stateUpdate *StateUpdate
// preimages holds the SHA3 preimages seen by the VM across the whole block:
// the pre/post-transaction system executions and every individual
// transaction execution. It is populated once via AddPreimages after the
// parallel execution has been collected.
preimages map[common.Hash][]byte
metrics BALStateTransitionMetrics metrics BALStateTransitionMetrics
maxBALIdx int maxBALIdx int
@ -108,6 +114,7 @@ func NewBALStateTransition(block *types.Block, prefetchReader Reader, db Databas
tries: sync.Map{}, tries: sync.Map{},
deletions: make(map[common.Address]struct{}), deletions: make(map[common.Address]struct{}),
stateUpdate: nil, stateUpdate: nil,
preimages: make(map[common.Hash][]byte),
maxBALIdx: len(block.Transactions()) + 1, maxBALIdx: len(block.Transactions()) + 1,
}, nil }, nil
} }
@ -533,7 +540,23 @@ func (s *BALStateTransition) IntermediateRoot(_ bool) common.Hash {
return s.rootHash return s.rootHash
} }
func (s *BALStateTransition) Preimages() map[common.Hash][]byte { // AddPreimages merges a set of SHA3 preimages into the block-level preimage
// TODO: implement this // set, leaving any hash already present untouched (mirroring the
return make(map[common.Hash][]byte) // first-write-wins behaviour of StateDB.AddPreimage). The parallel processor
// collects the preimages from the per-transaction and pre/post-transaction
// executions and installs them here once execution has completed.
func (s *BALStateTransition) AddPreimages(preimages map[common.Hash][]byte) {
for hash, preimage := range preimages {
if _, ok := s.preimages[hash]; !ok {
s.preimages[hash] = preimage
}
}
}
// Preimages returns the SHA3 preimages seen by the VM over the course of the
// block. Unlike the sequential processor, which reads them from a single
// statedb, the BAL processor executes the block across several statedbs and
// installs the combined set via AddPreimages.
func (s *BALStateTransition) Preimages() map[common.Hash][]byte {
return s.preimages
} }

View file

@ -0,0 +1,83 @@
// Copyright 2026 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package state
import (
"bytes"
"testing"
"github.com/ethereum/go-ethereum/common"
)
// newPreimageTransition returns a BALStateTransition with only the preimage
// accumulator initialised, which is all that AddPreimages/Preimages touch.
func newPreimageTransition() *BALStateTransition {
return &BALStateTransition{preimages: make(map[common.Hash][]byte)}
}
// TestBALStateTransitionPreimagesUnion verifies that AddPreimages folds the
// preimages contributed by the various block executions (the per-transaction
// copies plus the pre/post-tx execution) into a single set returned by
// Preimages.
func TestBALStateTransitionPreimagesUnion(t *testing.T) {
s := newPreimageTransition()
// Empty/nil inputs are no-ops.
s.AddPreimages(nil)
s.AddPreimages(map[common.Hash][]byte{})
if got := len(s.Preimages()); got != 0 {
t.Fatalf("expected empty set, got %d entries", got)
}
s.AddPreimages(map[common.Hash][]byte{
{0x01}: []byte("pre"),
{0x02}: []byte("tx1"),
{0x03}: []byte("tx2"),
{0x04}: []byte("post"),
})
want := map[common.Hash][]byte{
{0x01}: []byte("pre"),
{0x02}: []byte("tx1"),
{0x03}: []byte("tx2"),
{0x04}: []byte("post"),
}
got := s.Preimages()
if len(got) != len(want) {
t.Fatalf("preimage count: got %d, want %d", len(got), len(want))
}
for h, v := range want {
if !bytes.Equal(got[h], v) {
t.Errorf("preimage %x: got %q, want %q", h, got[h], v)
}
}
}
// TestBALStateTransitionPreimagesFirstWriteWins verifies that a preimage for a
// hash already present is not overwritten across successive AddPreimages calls,
// matching StateDB.AddPreimage.
func TestBALStateTransitionPreimagesFirstWriteWins(t *testing.T) {
s := newPreimageTransition()
key := common.Hash{0xaa}
s.AddPreimages(map[common.Hash][]byte{key: []byte("first")})
s.AddPreimages(map[common.Hash][]byte{key: []byte("second")})
if got := s.Preimages()[key]; !bytes.Equal(got, []byte("first")) {
t.Fatalf("expected first write to win, got %q", got)
}
}