Skip to content

Commit

Permalink
Fixing race condition in invariance checks (#1441)
Browse files Browse the repository at this point in the history
  • Loading branch information
slundqui authored Apr 26, 2024
1 parent 416b9ce commit a098db3
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 23 deletions.
7 changes: 3 additions & 4 deletions scripts/remote_fuzz_bot_invariant_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ def main(argv: Sequence[str] | None = None) -> None:
# Update block number
last_executed_block_number = latest_block_number
run_invariant_checks(
latest_block,
latest_block_number,
interface,
parsed_args.test_epsilon,
latest_block=latest_block,
interface=interface,
test_epsilon=parsed_args.test_epsilon,
raise_error_on_failure=False,
log_to_rollbar=log_to_rollbar,
)
Expand Down
7 changes: 3 additions & 4 deletions scripts/testnet_fuzz_bot_invariant_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,9 @@ def main(argv: Sequence[str] | None = None) -> None:
for name, hyperdrive_obj in hyperdrive_objs.items():
logging.info("Running invariance check on %s", name)
run_invariant_checks(
latest_block,
latest_block_number,
hyperdrive_obj.interface,
parsed_args.test_epsilon,
latest_block=latest_block,
interface=hyperdrive_obj.interface,
test_epsilon=parsed_args.test_epsilon,
raise_error_on_failure=False,
log_to_rollbar=log_to_rollbar,
pool_name=name,
Expand Down
19 changes: 9 additions & 10 deletions src/agent0/hyperfuzz/system_fuzz/invariant_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import logging
from typing import Any, NamedTuple, Sequence

from eth_typing import BlockNumber
from fixedpointmath import FixedPoint, isclose
from hexbytes import HexBytes
from web3.exceptions import BlockNotFound
Expand All @@ -23,7 +22,6 @@

def run_invariant_checks(
latest_block: BlockData,
latest_block_number: BlockNumber,
interface: HyperdriveReadInterface,
test_epsilon: float,
raise_error_on_failure: bool = False,
Expand All @@ -44,8 +42,6 @@ def run_invariant_checks(
---------
latest_block: BlockData
The current block to be tested.
latest_block_number: BlockNumber
The current block number.
interface: HyperdriveReadInterface
An instantiated HyperdriveReadInterface object constructed using the script arguments.
test_epsilon: float
Expand Down Expand Up @@ -73,9 +69,9 @@ def run_invariant_checks(
_check_total_shares(pool_state),
_check_minimum_share_reserves(pool_state),
_check_solvency(pool_state),
_check_present_value_greater_than_idle_shares(latest_block_number, interface, pool_state),
_check_lp_share_price(latest_block_number, interface, test_epsilon, pool_state),
_check_checkpointing_should_never_fail(latest_block, interface, pool_state),
_check_present_value_greater_than_idle_shares(interface, pool_state),
_check_lp_share_price(interface, test_epsilon, pool_state),
_check_checkpointing_should_never_fail(interface, pool_state),
_check_initial_lp_profitable(pool_state),
]

Expand Down Expand Up @@ -150,7 +146,7 @@ def _check_base_balances(pool_state: PoolState, is_steth: bool) -> InvariantChec


def _check_checkpointing_should_never_fail(
block: BlockData, interface: HyperdriveReadInterface, pool_state: PoolState
interface: HyperdriveReadInterface, pool_state: PoolState
) -> InvariantCheckResults:
# Creating a checkpoint should never fail
# TODO: add get_block_transactions() to interface
Expand All @@ -161,6 +157,8 @@ def _check_checkpointing_should_never_fail(
exception_message: str | None = None
exception_data: dict[str, Any] = {}

block = pool_state.block

transactions = block.get("transactions", None)
if transactions is not None and isinstance(transactions, Sequence):
# If any transaction is to hyperdrive then assert a checkpoint happened
Expand Down Expand Up @@ -262,7 +260,6 @@ def _check_total_shares(pool_state: PoolState) -> InvariantCheckResults:


def _check_present_value_greater_than_idle_shares(
block_number: BlockNumber,
interface: HyperdriveReadInterface,
pool_state: PoolState,
) -> InvariantCheckResults:
Expand All @@ -284,6 +281,7 @@ def _check_present_value_greater_than_idle_shares(
exception_data: dict[str, Any] = {}

present_value = interface.calc_present_value(pool_state)
block_number = pool_state.block_number
idle_shares = interface.get_idle_shares(block_number)

if not present_value >= idle_shares:
Expand All @@ -298,7 +296,6 @@ def _check_present_value_greater_than_idle_shares(


def _check_lp_share_price(
block_number: BlockNumber,
interface: HyperdriveReadInterface,
test_epsilon: float,
pool_state: PoolState,
Expand All @@ -323,6 +320,8 @@ def _check_lp_share_price(
exception_message = ""
exception_data: dict[str, Any] = {}

block_number = pool_state.block_number

# We expect the lp share price to be less than the test epsilon between sequential blocks
# However, when simulating, we can advance time by any amount of time. Hence, we define
# the test epsilon to be relative to 12 seconds (1 block), and normalize by the actual time
Expand Down
9 changes: 4 additions & 5 deletions src/agent0/hyperfuzz/system_fuzz/run_fuzz_bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,9 @@ def run_fuzz_bots(
if latest_block_number is None:
raise AssertionError("Block has no number.")
run_invariant_checks(
latest_block,
latest_block_number,
hyperdrive_pool.interface,
invariance_test_epsilon,
latest_block=latest_block,
interface=hyperdrive_pool.interface,
test_epsilon=invariance_test_epsilon,
raise_error_on_failure=raise_error_on_failed_invariance_checks,
log_to_rollbar=log_to_rollbar,
)
Expand Down Expand Up @@ -361,7 +360,7 @@ def run_fuzz_bots(
# initialize an rng object
assert hyperdrive_pool.config.rng is not None
# TODO should there be an upper bound for advancing time?
random_time = hyperdrive_pool.config.rng.integers(*ADVANCE_TIME_SECONDS_RANGE)
random_time = int(hyperdrive_pool.config.rng.integers(*ADVANCE_TIME_SECONDS_RANGE))
hyperdrive_pool.chain.advance_time(random_time, create_checkpoints=True)
else:
raise ValueError("Random advance time only allowed for pools deployed on ILocalChain")
Expand Down

0 comments on commit a098db3

Please sign in to comment.