Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,18 @@ func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrO
validate: opts.Validation,
fullTx: opts.ReturnFullTransactions,
}
if api.b.ChainConfig().IsOptimism() {
latestBlock, err := api.b.BlockByNumber(ctx, rpc.LatestBlockNumber)
if err != nil {
return nil, err
}
if len(latestBlock.Transactions()) > 0 {
// For the special case of the genesis block, there are no transactions
// so we won't set the l1AttributesTx and the simulation will
// likely fail.
sim.l1AttributesTx = latestBlock.Transactions()[0]
}
}
return sim.execute(ctx, opts.BlockStateCalls)
}

Expand Down
4 changes: 2 additions & 2 deletions internal/ethapi/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2639,7 +2639,7 @@ func TestSimulateV1ChainLinkage(t *testing.T) {
}
)

results, err := sim.execute(ctx, blocks)
results, err := sim.execute(ctx, blocks, nil)
if err != nil {
t.Fatalf("simulation execution failed: %v", err)
}
Expand Down Expand Up @@ -2703,7 +2703,7 @@ func TestSimulateV1TxSender(t *testing.T) {
{Calls: []TransactionArgs{
{From: &sender2, To: &recipient, Value: (*hexutil.Big)(big.NewInt(4000))},
}},
})
}, nil)
if err != nil {
t.Fatalf("simulation execution failed: %v", err)
}
Expand Down
34 changes: 33 additions & 1 deletion internal/ethapi/simulate.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/internal/ethapi/override"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
)

const (
Expand Down Expand Up @@ -174,9 +175,14 @@ type simulator struct {
traceTransfers bool
validate bool
fullTx bool

// OP-Stack diff
l1AttributesTx *types.Transaction
}

// execute runs the simulation of a series of blocks.
// OPStack-diff: execute accepts an l1 attributes transaction which (if non-nil) will be injected into each block
// at position 0.
func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlockResult, error) {
if err := ctx.Err(); err != nil {
return nil, err
Expand Down Expand Up @@ -220,6 +226,8 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlo
return results, nil
}

// OP-Stack diff: proceesBlock accepts an l1 attributes transaction which (if non-nil) will be injected into the block
// at position 0.
func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, timeout time.Duration) (*types.Block, []simCallResult, map[common.Hash]common.Address, types.Receipts, error) {
// Set header fields that depend only on parent block.
// Parent hash is needed for evm.GetHashFn to work.
Expand Down Expand Up @@ -359,12 +367,36 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
reqHash := types.CalcRequestsHash(requests)
header.RequestsHash = &reqHash
}
blockBody := &types.Body{Transactions: txes, Withdrawals: *block.BlockOverrides.Withdrawals}

// For Optimism blocks, inject the provided l1 attributes transaction at the beginning of the block.
// This is required because CalcDAFootprint (called by FinalizeAndAssemble for Jovian blocks)
// expects the first transaction to be a deposit transaction containing L1 attributes data.
isOptimism := sim.chainConfig.IsOptimism()
finalTxes := txes
if isOptimism && sim.l1AttributesTx != nil {
finalTxes = append([]*types.Transaction{sim.l1AttributesTx}, txes...)
}

blockBody := &types.Body{Transactions: finalTxes, Withdrawals: *block.BlockOverrides.Withdrawals}
chainHeadReader := &simChainHeadReader{ctx, sim.b}
b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts)
if err != nil {
return nil, nil, nil, nil, err
}

// For Optimism blocks, reconstruct the block without the l1 attributes transaction
// to maintain consistent indexing between transactions and receipts.
// We must use types.NewBlock to recompute TxHash correctly for the user transactions only.
if isOptimism && sim.l1AttributesTx != nil {
b = types.NewBlock(
b.Header(),
&types.Body{Transactions: txes, Withdrawals: *block.BlockOverrides.Withdrawals},
receipts,
trie.NewStackTrie(nil),
sim.chainConfig,
)
}

repairLogs(callResults, b.Hash())
return b, callResults, senders, receipts, nil
}
Expand Down