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

Native Bridge Supply Monitoring #148

@ethenotethan

Description

@ethenotethan

Heuristic Description

A heuristic should exist that monitors for discrepancies between the locked ETH amount on the L1 OptimismPortal and the unlocked amount on L2. This allows for deep introspection into the safety of the native bridge and allows chain operators to detect real-time anomalies.

Consideration(s)

  1. ETH can be burnt arbitrarily on L2 by either sending it to the 0x000 blackhole address or immobilizing it via SELFDESTRUCT operations that specify the calling contract as the beneficiary. This could result in potential discrepancies between the circulating supply on L2 and the locked supply on L1. Burns can also be trigged by anyone on the L2ToL1MessagePasser contract to delete initiated withdrawal funds.
  2. This implementation requires having access to all historical L1/L2 bridge events from genesis of the L2 chain (first L2 block, first deposit transaction block on L1). Unfortunately, Pessimism has no way to persist this data nor does it have efficient backfilling capabilities. Integrating with the OP Indexer is required for seamless unblocking.

There are a few ways to calculate variations of the l1 supply:
prospective - Compute using initialized withdrawals from L2
Intermittent - Compute using proven withdrawals on L1
literal - Compute using accredited withdrawals on L1
Each of these can be subtracted by either the deposit sum or the OptimismPortal contract ETH value to represent an L1 supply.

Pseudocode

The following pseudocode demonstrates a high level implementation for how to compute different native bridge supplies on both L1 and L2. Additionally, it showcases a lightweight heuristic analysis example.

# Get L1 deposit supply by traversing all op_portal deposit events
def get_l1_deposit_supply() -> int:
    deposit_events = get_deposit_events(op_portal)
     # We use mint as it indicates funds being introduced to L2 circulation
    return sum([ e for event.mint in deposit_events])

# Get L2 deposit supply via summating all deposit tx type values
def get_l2_deposit_supply() -> int:
  all_deposits = get_deposit_txs()
  deposit_sum = sum([e for e.mint in all_deposits])
  return deposit_sum
  

def get_l2_withdrawal_supply() -> int:
   initiated_withdrawals = get_initiated_withdrawals()
   amt = 0
   for withdrawal in initiated_withdrawals:
      amt+=withdrawal.value
   
   return amt

# Get L1 withdrawal supply via iterating through proven_withdrawals
# on L1 and finding the associated message event on L1 that holds the
# the ETH value, summate when found
def get_l1_withdrawal_supply() -> int:
   proven_withdrawals = get_l1_proven_withdrawals()
   amt = 0
   for withdrawal in proven_withdrawals:
     amt+=withdrawal.value

   return amt

# Gets the total amount of ETH burnt via the L2ToL1MessagePasser on L2 
def get_l2_burn_supply(message_passer) -> int:
   amt = 0
   burns = get_burn_events(message_passer)
   for event in burns:
      amt += event.value
   
    # lets assume any ETH locked in the contract will inevitably be burnt
    amt += message_passer.value()
   return amt

# Compute L1 supply by subtracting the amount burnt from the total amount deposited
# Note - there will be a slight discrepancy given that the l2_withdrawal_supply will likely
# have withdrawals that haven't been accredited/proven on L1 yet
prospective_l1_supply = get_l1_deposit_supply() - get_l2_withdrawal_supply()

# Compute L1 supply by subtracting the amount in proven withdrawals from the total amount
# Note - This will require correlating a withdrawal hash on L1 to a sentMessage on L2
l1_supply_waiting = get_l1_deposit_supply() - get_l1_proven_withdrawal_amount()

# Get the actual L1 supply by getting the ETH amount for the op_portal
actual_l1_supply = optimism_portal.balance()

# Compute L2 supply by subtracting the amount deposited from the amount burnt
# by the L2ToL1MessagePasser contract
l2_supply = get_l2_deposit_supply() - get_l2_burn_supply()

## Run invariant analysis
# NOTE - Assume x is a set of user defined float inputs per each invariant

# I0 
if percent_diff(actual_l1_supply, l1_supply_waiting) > x0:
   ALERT()
   
#I1
if percent_diff(actual_l1_supply, prospective_l1_supply) > x1:
   ALERT()
   
#I2
if percent_diff(prospective_l1_supply, l2_supply) > x2:
   ALERT()

Metadata

Metadata

Assignees

Labels

heuristicA request for a new heuristic

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions