diff --git a/crates/optimism/tests/proofs/resyncing_test.go b/crates/optimism/tests/proofs/resyncing_test.go new file mode 100644 index 00000000000..18b71843996 --- /dev/null +++ b/crates/optimism/tests/proofs/resyncing_test.go @@ -0,0 +1,63 @@ +package proofs + +import ( + "testing" + "time" + + "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/presets" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" +) + +func TestResyncing(gt *testing.T) { + t := devtest.SerialT(gt) + ctx := t.Ctx() + + sys := presets.NewSingleChainMultiNode(t) + + alice := sys.FunderL2.NewFundedEOA(eth.OneEther) + bob := sys.FunderL2.NewFundedEOA(eth.OneEther) + + tx := alice.Transfer(bob.Address(), eth.OneHundredthEther) + receipt, err := tx.Included.Eval(ctx) + require.NoError(gt, err) + require.Equal(gt, types.ReceiptStatusSuccessful, receipt.Status) + + t.Logf("Stopping L2 CLB and ELB to simulate downtime") + sys.L2CLB.Stop() + sys.L2ELB.Stop() + + var blockNumbers []uint64 + // produce some transactions while the node is down + for i := 0; i < 5; i++ { + tx := alice.Transfer(bob.Address(), eth.OneHundredthEther) + receipt, err := tx.Included.Eval(ctx) + require.NoError(gt, err) + require.Equal(gt, types.ReceiptStatusSuccessful, receipt.Status) + blockNumbers = append(blockNumbers, receipt.BlockNumber.Uint64()) + } + + // restart the node and ensure it can sync the missing blocks + t.Logf("Restarting L2 CLB and ELB to resync") + sys.L2ELB.Start() + sys.L2CLB.Start() + + time.Sleep(3 * time.Second) + + err = wait.For(t.Ctx(), 2*time.Second, func() (bool, error) { + status := sys.L2CLB.SyncStatus() + return status.UnsafeL2.Number > blockNumbers[len(blockNumbers)-1], nil + }) + require.NoError(gt, err, "L2 CLB failed to resync to latest block") + + t.Logf("Fetching and verifying proofs for transactions produced while node was down") + // verify the proofs for the transactions produced while the node was down + for _, blockNumber := range blockNumbers { + fetchAndVerifyProofs(t, sys, bob.Address(), []common.Hash{}, blockNumber) + fetchAndVerifyProofs(t, sys, alice.Address(), []common.Hash{}, blockNumber) + } +} diff --git a/crates/optimism/tests/proofs/simple_storage_test.go b/crates/optimism/tests/proofs/simple_storage_test.go index 0d4f2323090..f5c23fde01b 100644 --- a/crates/optimism/tests/proofs/simple_storage_test.go +++ b/crates/optimism/tests/proofs/simple_storage_test.go @@ -60,7 +60,7 @@ func TestStorageProofUsingSimpleStorageContract(gt *testing.T) { t.Logf("contract deployed at address %s in L2 block %d", contractAddress.Hex(), blockNum) // fetch and verify initial proof (should be zeroed storage) - fetchAndVerifyProofs(ctx, t, sys, contractAddress, []common.Hash{common.HexToHash("0x0")}, blockNum) + fetchAndVerifyProofs(t, sys, contractAddress, []common.Hash{common.HexToHash("0x0")}, blockNum) type caseEntry struct { Block uint64 @@ -88,12 +88,12 @@ func TestStorageProofUsingSimpleStorageContract(gt *testing.T) { // for each case, get proof and verify for _, c := range cases { - fetchAndVerifyProofs(ctx, t, sys, contractAddress, []common.Hash{common.HexToHash("0x0")}, c.Block) + fetchAndVerifyProofs(t, sys, contractAddress, []common.Hash{common.HexToHash("0x0")}, c.Block) } // test with non-existent storage slot nonExistentSlot := common.HexToHash("0xdeadbeef") - fetchAndVerifyProofs(ctx, t, sys, contractAddress, []common.Hash{nonExistentSlot}, cases[len(cases)-1].Block) + fetchAndVerifyProofs(t, sys, contractAddress, []common.Hash{nonExistentSlot}, cases[len(cases)-1].Block) } func multiStorageSetValues(t devtest.T, parsedABI *abi.ABI, user *dsl.EOA, contractAddress common.Address, aVal, bVal *big.Int) *types.Receipt { @@ -142,7 +142,7 @@ func TestStorageProofUsingMultiStorageContract(gt *testing.T) { t.Logf("contract deployed at address %s in L2 block %d", contractAddress.Hex(), blockNum) // fetch and verify initial proof (should be zeroed storage) - fetchAndVerifyProofs(ctx, t, sys, contractAddress, []common.Hash{common.HexToHash("0x0"), common.HexToHash("0x1")}, blockNum) + fetchAndVerifyProofs(t, sys, contractAddress, []common.Hash{common.HexToHash("0x0"), common.HexToHash("0x1")}, blockNum) // set multiple storage slots type caseEntry struct { @@ -184,7 +184,7 @@ func TestStorageProofUsingMultiStorageContract(gt *testing.T) { slots = append(slots, slot) } - fetchAndVerifyProofs(ctx, t, sys, contractAddress, slots, c.Block) + fetchAndVerifyProofs(t, sys, contractAddress, slots, c.Block) } } @@ -322,11 +322,11 @@ func TestTokenVaultStorageProofs(gt *testing.T) { // fetch & verify proofs at appropriate blocks // balance after deposit (depositBlock) t.Logf("Verifying balance slot %s at deposit block %d", balanceSlot.Hex(), depositBlock) - fetchAndVerifyProofs(ctx, t, sys, contractAddr, []common.Hash{balanceSlot, depositor0Slot}, depositBlock) + fetchAndVerifyProofs(t, sys, contractAddr, []common.Hash{balanceSlot, depositor0Slot}, depositBlock) // allowance after approve (approveBlock) t.Logf("Verifying allowance slot %s at approve block %d", allowanceSlot.Hex(), approveBlock) - fetchAndVerifyProofs(ctx, t, sys, contractAddr, []common.Hash{allowanceSlot}, approveBlock) + fetchAndVerifyProofs(t, sys, contractAddr, []common.Hash{allowanceSlot}, approveBlock) // after deactivation, allowance should be zero at deactBlock t.Logf("Verifying allowance slot %s at deactivate block %d", allowanceSlot.Hex(), deactBlock) - fetchAndVerifyProofs(ctx, t, sys, contractAddr, []common.Hash{allowanceSlot}, deactBlock) + fetchAndVerifyProofs(t, sys, contractAddr, []common.Hash{allowanceSlot}, deactBlock) } diff --git a/crates/optimism/tests/proofs/utils.go b/crates/optimism/tests/proofs/utils.go index 74aeb7e30e2..be89cbd5e74 100644 --- a/crates/optimism/tests/proofs/utils.go +++ b/crates/optimism/tests/proofs/utils.go @@ -153,7 +153,8 @@ func VerifyProof(res *eth.AccountResult, stateRoot common.Hash) error { return err } -func fetchAndVerifyProofs(ctx context.Context, t devtest.T, sys *presets.SingleChainMultiNode, contractAddress common.Address, slots []common.Hash, block uint64) { +func fetchAndVerifyProofs(t devtest.T, sys *presets.SingleChainMultiNode, contractAddress common.Address, slots []common.Hash, block uint64) { + ctx := t.Ctx() gethProofRes, err := sys.L2EL.Escape().L2EthClient().GetProof(ctx, contractAddress, slots, hexutil.Uint64(block).String()) if err != nil { t.Errorf("failed to get proof from L2EL at block %d: %v", block, err)