diff --git a/indexer/integration_tests/bedrock_test.go b/indexer/integration_tests/bedrock_test.go index 9a5e8a25cdd62..61d21c9934055 100644 --- a/indexer/integration_tests/bedrock_test.go +++ b/indexer/integration_tests/bedrock_test.go @@ -201,14 +201,10 @@ func TestBedrockIndexer(t *testing.T) { require.NoError(t, err) proofCl := gethclient.New(rpcClient) receiptCl := ethclient.NewClient(rpcClient) - wParams, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, wdTx.Hash(), finHeader) - require.NoError(t, err) - oracle, err := bindings.NewL2OutputOracleCaller(predeploys.DevL2OutputOracleAddr, l1Client) require.Nil(t, err) - - l2OutputIndex, err := oracle.GetL2OutputIndexAfter(&bind.CallOpts{}, wParams.BlockNumber) - require.Nil(t, err) + wParams, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, wdTx.Hash(), finHeader, oracle) + require.NoError(t, err) l1Opts.Value = big.NewInt(0) // Prove our withdrawal @@ -222,7 +218,7 @@ func TestBedrockIndexer(t *testing.T) { GasLimit: wParams.GasLimit, Data: wParams.Data, }, - l2OutputIndex, + wParams.L2OutputIndex, wParams.OutputRootProof, wParams.WithdrawalProof, ) @@ -236,7 +232,7 @@ func TestBedrockIndexer(t *testing.T) { e2eutils.TimeoutCtx(t, time.Minute), l1Client, predeploys.DevOptimismPortalAddr, - wParams.BlockNumber, + finHeader.Number, ) require.NoError(t, err) diff --git a/op-e2e/actions/user.go b/op-e2e/actions/user.go index 536882bbe9a2a..39361e9624c58 100644 --- a/op-e2e/actions/user.go +++ b/op-e2e/actions/user.go @@ -409,7 +409,7 @@ func (s *CrossLayerUser) ProveWithdrawal(t Testing, l2TxHash common.Hash) common // We generate a proof for the latest L2 output, which shouldn't require archive-node data if it's recent enough. header, err := s.L2.env.EthCl.HeaderByNumber(t.Ctx(), l2OutputBlockNr) require.NoError(t, err) - params, err := withdrawals.ProveWithdrawalParameters(t.Ctx(), s.L2.env.Bindings.ProofClient, s.L2.env.EthCl, s.lastL2WithdrawalTxHash, header) + params, err := withdrawals.ProveWithdrawalParameters(t.Ctx(), s.L2.env.Bindings.ProofClient, s.L2.env.EthCl, s.lastL2WithdrawalTxHash, header, &s.L1.env.Bindings.L2OutputOracle.L2OutputOracleCaller) require.NoError(t, err) // Create the prove tx @@ -480,7 +480,7 @@ func (s *CrossLayerUser) CompleteWithdrawal(t Testing, l2TxHash common.Hash) com // params for the `WithdrawalTransaction` type generated in the bindings. header, err := s.L2.env.EthCl.HeaderByNumber(t.Ctx(), l2OutputBlockNr) require.NoError(t, err) - params, err := withdrawals.ProveWithdrawalParameters(t.Ctx(), s.L2.env.Bindings.ProofClient, s.L2.env.EthCl, s.lastL2WithdrawalTxHash, header) + params, err := withdrawals.ProveWithdrawalParameters(t.Ctx(), s.L2.env.Bindings.ProofClient, s.L2.env.EthCl, s.lastL2WithdrawalTxHash, header, &s.L1.env.Bindings.L2OutputOracle.L2OutputOracleCaller) require.NoError(t, err) // Create the withdrawal tx diff --git a/op-e2e/system_test.go b/op-e2e/system_test.go index eca92e8c262f1..2cbd399554587 100644 --- a/op-e2e/system_test.go +++ b/op-e2e/system_test.go @@ -836,16 +836,13 @@ func TestWithdrawals(t *testing.T) { receiptCl := ethclient.NewClient(rpcClient) // Now create withdrawal - params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, tx.Hash(), header) - require.Nil(t, err) - - portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) + oracle, err := bindings.NewL2OutputOracleCaller(predeploys.DevL2OutputOracleAddr, l1Client) require.Nil(t, err) - oracle, err := bindings.NewL2OutputOracleCaller(predeploys.DevL2OutputOracleAddr, l1Client) + params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, tx.Hash(), header, oracle) require.Nil(t, err) - l2OutputIndex, err := oracle.GetL2OutputIndexAfter(&bind.CallOpts{}, params.BlockNumber) + portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) require.Nil(t, err) opts.Value = nil @@ -861,7 +858,7 @@ func TestWithdrawals(t *testing.T) { GasLimit: params.GasLimit, Data: params.Data, }, - l2OutputIndex, + params.L2OutputIndex, params.OutputRootProof, params.WithdrawalProof, ) @@ -875,7 +872,7 @@ func TestWithdrawals(t *testing.T) { // Wait for finalization and then create the Finalized Withdrawal Transaction ctx, cancel = context.WithTimeout(context.Background(), 20*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) defer cancel() - _, err = withdrawals.WaitForFinalizationPeriod(ctx, l1Client, predeploys.DevOptimismPortalAddr, params.BlockNumber) + _, err = withdrawals.WaitForFinalizationPeriod(ctx, l1Client, predeploys.DevOptimismPortalAddr, header.Number) require.Nil(t, err) // Finalize withdrawal diff --git a/op-node/withdrawals/utils.go b/op-node/withdrawals/utils.go index 3a5f11c03d93d..21ac82bd391d3 100644 --- a/op-node/withdrawals/utils.go +++ b/op-node/withdrawals/utils.go @@ -137,16 +137,16 @@ type ProvenWithdrawalParameters struct { Target common.Address Value *big.Int GasLimit *big.Int - BlockNumber *big.Int + L2OutputIndex *big.Int Data []byte OutputRootProof bindings.TypesOutputRootProof WithdrawalProof [][]byte // List of trie nodes to prove L2 storage } -// ProveWithdrawalParameters queries L2 to generate all withdrawal parameters and proof necessary to prove a withdrawal on L1. +// ProveWithdrawalParameters queries L1 & L2 to generate all withdrawal parameters and proof necessary to prove a withdrawal on L1. // The header provided is very important. It should be a block (timestamp) for which there is a submitted output in the L2 Output Oracle // contract. If not, the withdrawal will fail as it the storage proof cannot be verified if there is no submitted state root. -func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2ReceiptCl ReceiptClient, txHash common.Hash, header *types.Header) (ProvenWithdrawalParameters, error) { +func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2ReceiptCl ReceiptClient, txHash common.Hash, header *types.Header, l2OutputOracleContract *bindings.L2OutputOracleCaller) (ProvenWithdrawalParameters, error) { // Transaction receipt receipt, err := l2ReceiptCl.TransactionReceipt(ctx, txHash) if err != nil { @@ -170,6 +170,12 @@ func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Recei if err != nil { return ProvenWithdrawalParameters{}, err } + + // Fetch the L2OutputIndex from the L2 Output Oracle caller (on L1) + l2OutputIndex, err := l2OutputOracleContract.GetL2OutputIndexAfter(&bind.CallOpts{}, header.Number) + if err != nil { + return ProvenWithdrawalParameters{}, fmt.Errorf("failed to get l2OutputIndex: %w", err) + } // TODO: Could skip this step, but it's nice to double check it err = VerifyProof(header.Root, p) if err != nil { @@ -186,13 +192,13 @@ func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Recei } return ProvenWithdrawalParameters{ - Nonce: ev.Nonce, - Sender: ev.Sender, - Target: ev.Target, - Value: ev.Value, - GasLimit: ev.GasLimit, - BlockNumber: new(big.Int).Set(header.Number), - Data: ev.Data, + Nonce: ev.Nonce, + Sender: ev.Sender, + Target: ev.Target, + Value: ev.Value, + GasLimit: ev.GasLimit, + L2OutputIndex: l2OutputIndex, + Data: ev.Data, OutputRootProof: bindings.TypesOutputRootProof{ Version: [32]byte{}, // Empty for version 1 StateRoot: header.Root,