Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
13 changes: 12 additions & 1 deletion internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,18 @@ func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrO
validate: opts.Validation,
fullTx: opts.ReturnFullTransactions,
}
return sim.execute(ctx, opts.BlockStateCalls)
var l1AttributesTx *types.Transaction
if api.b.ChainConfig().IsOptimism() {
latestBlock, err := api.b.BlockByNumber(ctx, rpc.LatestBlockNumber)
if err != nil {
return nil, err
}
if len(latestBlock.Transactions()) == 0 {
return nil, errors.New("optimism: no L1 attributes transaction found in latest block")
}
l1AttributesTx = latestBlock.Transactions()[0]
}
return sim.execute(ctx, opts.BlockStateCalls, l1AttributesTx)
}

// DoEstimateGas returns the lowest possible gas limit that allows the transaction to run
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
37 changes: 33 additions & 4 deletions 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 @@ -177,7 +178,9 @@ type simulator struct {
}

// execute runs the simulation of a series of blocks.
func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlockResult, error) {
// 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, l1AttributesTx *types.Transaction) ([]*simBlockResult, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
Expand Down Expand Up @@ -209,7 +212,7 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlo
parent = sim.base
)
for bi, block := range blocks {
result, callResults, senders, receipts, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout)
result, callResults, senders, receipts, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout, l1AttributesTx)
if err != nil {
return nil, err
}
Expand All @@ -220,7 +223,9 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlo
return results, nil
}

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) {
// 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, l1AttributesTransaction *types.Transaction) (*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.
header.ParentHash = parent.Hash()
Expand Down Expand Up @@ -359,12 +364,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 {
finalTxes = append([]*types.Transaction{l1AttributesTransaction}, 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 {
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