Skip to content
This repository was archived by the owner on May 29, 2024. It is now read-only.

Commit 41bc9fb

Browse files
ethenotethanEthen Pociaskadrain-cb
authored
Withdrawal safety for all key L2->L1 events (#186)
Co-authored-by: Ethen Pociask <[email protected]> Co-authored-by: Adrian Smith <[email protected]>
1 parent 346ec30 commit 41bc9fb

File tree

8 files changed

+398
-173
lines changed

8 files changed

+398
-173
lines changed

e2e/heuristic_test.go

+85-34
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ func TestContractEvent(t *testing.T) {
224224

225225
// TestWithdrawalSafetyAllInvariants ... Tests the E2E flow of a withdrawal
226226
// safety heuristic session. This test ensures that an alert is produced in the event
227-
// of a highly suspicious withdrawal.
227+
// of a highly suspicious withdrawal at every step of the withdrawal flow.
228228
func TestWithdrawalSafetyAllInvariants(t *testing.T) {
229229
ts := e2e.CreateSysTestSuite(t, "")
230230
defer ts.Close()
@@ -259,6 +259,22 @@ func TestWithdrawalSafetyAllInvariants(t *testing.T) {
259259
core.L2ToL1MessagePasser: fakeAddr.String(),
260260
},
261261
},
262+
{
263+
Network: core.Layer2.String(),
264+
HeuristicType: core.WithdrawalSafety.String(),
265+
StartHeight: nil,
266+
EndHeight: nil,
267+
AlertingParams: &core.AlertPolicy{
268+
Sev: core.LOW.String(),
269+
Msg: alertMsg,
270+
},
271+
SessionParams: map[string]interface{}{
272+
"threshold": 0.20,
273+
"coefficient_threshold": 0.20,
274+
core.L1Portal: ts.Cfg.L1Deployments.OptimismPortalProxy.String(),
275+
core.L2ToL1MessagePasser: predeploys.L2ToL1MessagePasserAddr.String(),
276+
},
277+
},
262278
})
263279
require.NoError(t, err, "Error bootstrapping heuristic session")
264280

@@ -284,12 +300,33 @@ func TestWithdrawalSafetyAllInvariants(t *testing.T) {
284300
_, err = wait.ForReceiptOK(context.Background(), ts.L1Client, depositTx.Hash())
285301
require.NoError(t, err)
286302

287-
// Initiate and prove a withdrawal
303+
// Initiate withdrawal
288304
withdrawTx, err := l2ToL1MessagePasser.InitiateWithdrawal(l2Opts, aliceAddr, big.NewInt(100_000), calldata)
289305
require.NoError(t, err)
290-
withdrawReceipt, err := wait.ForReceiptOK(context.Background(), ts.L2Client, withdrawTx.Hash())
306+
initReceipt, err := wait.ForReceiptOK(context.Background(), ts.L2Client, withdrawTx.Hash())
291307
require.NoError(t, err)
292308

309+
// Wait for Pessimism to process initiation
310+
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
311+
id := ids[0].PathID
312+
height, err := ts.Subsystems.PathHeight(id)
313+
if err != nil {
314+
return false, err
315+
}
316+
317+
return height != nil && height.Uint64() > initReceipt.BlockNumber.Uint64(), nil
318+
}))
319+
320+
// Ensure Pessimism has detected what it considers an unsafe withdrawal
321+
alerts := ts.TestSlackSvr.SlackAlerts()
322+
require.Equal(t, 1, len(alerts), "expected 1 alerts")
323+
assert.Contains(t, alerts[0].Text, core.WithdrawalSafety.String(), "expected alert to be for withdrawal_safety")
324+
assert.Contains(t, alerts[0].Text, alertMsg, "expected alert to have alert message")
325+
326+
// Ensure that specific invariant messages are included in the alert
327+
assert.Contains(t, alerts[0].Text, alertMsg, registry.GreaterThanPortal)
328+
329+
ts.TestSlackSvr.ClearAlerts()
293330
// Mock the indexer call to return a really high withdrawal amount
294331
ts.TestIxClient.EXPECT().GetAllWithdrawalsByAddress(gomock.Any()).Return([]api_mods.WithdrawalItem{
295332
{
@@ -298,7 +335,7 @@ func TestWithdrawalSafetyAllInvariants(t *testing.T) {
298335
},
299336
}, nil).AnyTimes()
300337

301-
_, proveReceipt := op_e2e.ProveWithdrawal(t, *ts.Cfg, ts.L1Client, ts.Sys.EthInstances["sequencer"], ts.Cfg.Secrets.Alice, withdrawReceipt)
338+
params, proveReceipt := op_e2e.ProveWithdrawal(t, *ts.Cfg, ts.L1Client, ts.Sys.EthInstances["sequencer"], ts.Cfg.Secrets.Alice, initReceipt)
302339

303340
// Wait for Pessimism to process the proven withdrawal and send a notification to the mocked Slack server.
304341
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
@@ -312,7 +349,7 @@ func TestWithdrawalSafetyAllInvariants(t *testing.T) {
312349
}))
313350

314351
// Ensure Pessimism has detected what it considers an unsafe withdrawal
315-
alerts := ts.TestSlackSvr.SlackAlerts()
352+
alerts = ts.TestSlackSvr.SlackAlerts()
316353
require.Equal(t, 1, len(alerts), "expected 1 alerts")
317354
assert.Contains(t, alerts[0].Text, core.WithdrawalSafety.String(), "expected alert to be for withdrawal_safety")
318355
assert.Contains(t, alerts[0].Text, fakeAddr.String(), "expected alert to be for dummy L2ToL1MessagePasser")
@@ -323,34 +360,34 @@ func TestWithdrawalSafetyAllInvariants(t *testing.T) {
323360
assert.Contains(t, alerts[0].Text, alertMsg, registry.GreaterThanPortal)
324361
assert.Contains(t, alerts[0].Text, alertMsg, fmt.Sprintf(registry.GreaterThanThreshold, 20.0))
325362

326-
// TODO(#178) - Feat - Support WithdrawalProven processing in withdrawal_safety heuristic
327-
// Mock the indexer call to return a really low withdrawal amount
328-
// ts.TestIxClient.EXPECT().GetAllWithdrawalsByAddress(gomock.Any()).Return([]api_mods.WithdrawalItem{
329-
// {
330-
// TransactionHash: "0x123",
331-
// Amount: "1",
332-
// },
333-
// }, nil).AnyTimes()
363+
ts.TestIxClient.EXPECT().GetAllWithdrawalsByAddress(gomock.Any()).Return([]api_mods.WithdrawalItem{
364+
{
365+
TransactionHash: "0x123",
366+
Amount: "1",
367+
},
368+
}, nil).AnyTimes()
334369

335370
// Finalize the withdrawal
336-
// finalizeReceipt := op_e2e.FinalizeWithdrawal(t, *ts.Cfg, ts.L1Client, ts.Cfg.Secrets.Alice, proveReceipt, proveParams)
337-
338-
// // Wait for Pessimism to process the finalized withdrawal and send a notification to the mocked Slack server.
339-
// require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
340-
// id := ids[0].PathID
341-
// height, err := ts.Subsystems.PathHeight(id)
342-
// if err != nil {
343-
// return false, err
344-
// }
345-
346-
// return height.Uint64() > finalizeReceipt.BlockNumber.Uint64(), nil
347-
// }))
348-
349-
// alerts = ts.TestSlackSvr.SlackAlerts()
350-
// require.Equal(t, 3, len(alerts), "expected 3 alerts")
351-
// assert.Contains(t, alerts[0].Text, "unsafe_withdrawal", "expected alert to be for unsafe_withdrawal")
352-
// assert.Contains(t, alerts[0].Text, fakeAddr.String(), "expected alert to be for dummy L2ToL1MessagePasser")
353-
// assert.Contains(t, alerts[0].Text, alertMsg, "expected alert to have alert message")
371+
finalizeReceipt := op_e2e.FinalizeWithdrawal(t, *ts.Cfg, ts.L1Client, ts.Cfg.Secrets.Alice, proveReceipt, params)
372+
373+
// Wait for Pessimism to process the finalized withdrawal and send a notification to the mocked Slack server.
374+
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
375+
id := ids[0].PathID
376+
height, err := ts.Subsystems.PathHeight(id)
377+
if err != nil {
378+
return false, err
379+
}
380+
381+
return height.Uint64() > finalizeReceipt.BlockNumber.Uint64(), nil
382+
}))
383+
384+
alerts = ts.TestSlackSvr.SlackAlerts()
385+
require.Equal(t, 1, len(alerts), "expected 1 alert")
386+
assert.Contains(t, alerts[0].Text, core.WithdrawalSafety.String())
387+
assert.Contains(t, alerts[0].Text, alertMsg, "expected alert to have alert message")
388+
389+
// Ensure that specific invariant messages are included in the alert
390+
assert.Contains(t, alerts[0].Text, alertMsg, registry.TooSimilarToMax)
354391
}
355392

356393
// TestWithdrawalSafetyNoInvariants ... Verify that no alerts are produced in the event
@@ -418,7 +455,7 @@ func TestWithdrawalSafetyNoInvariants(t *testing.T) {
418455
},
419456
}, nil).AnyTimes()
420457

421-
_, proveReceipt := op_e2e.ProveWithdrawal(t, *ts.Cfg, ts.L1Client, ts.Sys.EthInstances["sequencer"], ts.Cfg.Secrets.Alice, withdrawReceipt)
458+
params, proveReceipt := op_e2e.ProveWithdrawal(t, *ts.Cfg, ts.L1Client, ts.Sys.EthInstances["sequencer"], ts.Cfg.Secrets.Alice, withdrawReceipt)
422459

423460
// Wait for Pessimism to process the proven withdrawal and send a notification to the mocked Slack server.
424461
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
@@ -431,9 +468,23 @@ func TestWithdrawalSafetyNoInvariants(t *testing.T) {
431468
return height != nil && height.Uint64() > proveReceipt.BlockNumber.Uint64(), nil
432469
}))
433470

434-
// Ensure that this withdrawal triggered no alerts
471+
// Finalize the withdrawal
472+
finalizeReceipt := op_e2e.FinalizeWithdrawal(t, *ts.Cfg, ts.L1Client, ts.Cfg.Secrets.Alice, proveReceipt, params)
473+
474+
// Wait for Pessimism to process the finalized withdrawal and send a notification to the mocked Slack server.
475+
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
476+
id := ids[0].PathID
477+
height, err := ts.Subsystems.PathHeight(id)
478+
if err != nil {
479+
return false, err
480+
}
481+
482+
return height.Uint64() > finalizeReceipt.BlockNumber.Uint64(), nil
483+
}))
484+
485+
// Ensure that this withdrawal flow triggered no alerts
435486
alerts := ts.TestSlackSvr.SlackAlerts()
436-
require.Equal(t, 0, len(alerts), "expected 0 alerts")
487+
require.Equal(t, 0, len(alerts))
437488
}
438489

439490
// TestFaultDetector ... Ensures that an alert is produced in the presence of a faulty L2Output root

internal/api/models/heuristic.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ type SessionRequestParams struct {
5353

5454
// Params ... Returns the heuristic session params
5555
func (hrp *SessionRequestParams) Params() *core.SessionParams {
56-
isp := core.NewSessionParams()
56+
isp := core.NewSessionParams(hrp.NetworkType())
5757

5858
for k, v := range hrp.SessionParams {
5959
isp.SetValue(k, v)

internal/core/core.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ const (
102102

103103
// SessionParams ... Parameters used to initialize a heuristic session
104104
type SessionParams struct {
105+
Net Network
105106
params map[string]any
106107
}
107108

@@ -112,8 +113,9 @@ func (sp *SessionParams) Bytes() []byte {
112113
}
113114

114115
// NewSessionParams ... Initializes heuristic session params
115-
func NewSessionParams() *SessionParams {
116+
func NewSessionParams(n Network) *SessionParams {
116117
isp := &SessionParams{
118+
Net: n,
117119
params: make(map[string]any, 0),
118120
}
119121
isp.params[NestedArgs] = []any{}

internal/core/core_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func Test_EngineRelay(t *testing.T) {
4949
}
5050

5151
func Test_SessionParams(t *testing.T) {
52-
isp := core.NewSessionParams()
52+
isp := core.NewSessionParams(core.Layer1)
5353
assert.NotNil(t, isp, "SessionParams should not be nil")
5454

5555
isp.SetValue("tst", "tst")

0 commit comments

Comments
 (0)