Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test to showcase that job-creator would profit from liars #442

Merged
merged 28 commits into from
Oct 3, 2024
Merged
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c207cd4
Integrate Tenderly to local_web3
kongzii Sep 26, 2024
6e352b0
lint
kongzii Sep 26, 2024
1cdb9f7
Add a new contract used for debugging
kongzii Sep 26, 2024
7493519
Add a new validator to convert blockchain timestamps to correct datetime
kongzii Sep 26, 2024
28e68b8
lint
kongzii Sep 26, 2024
4159c71
Dont be quiet
kongzii Sep 26, 2024
3b4708b
lint
kongzii Sep 26, 2024
4da7b62
Create OmenMarket out of blockchain events
kongzii Sep 26, 2024
48258fe
fix creation timestamp
kongzii Sep 26, 2024
1fef71e
Add test to showcase that job-creator would profit from liars
kongzii Sep 26, 2024
d8337d0
typo
kongzii Sep 26, 2024
4c34b40
more typos
kongzii Sep 26, 2024
5a83924
typooo
kongzii Sep 26, 2024
0312c55
Use `mint_new_block`
kongzii Sep 30, 2024
75d1f1e
lower the difference
kongzii Sep 30, 2024
c3feff1
Merge branch 'main' into peter/debugging-contract
kongzii Sep 30, 2024
e869421
fix circular import
kongzii Sep 30, 2024
4930a23
fix another test
kongzii Sep 30, 2024
ec70b35
be less strict
kongzii Sep 30, 2024
51ac55f
lint
kongzii Sep 30, 2024
2484dfb
Merge branch 'peter/debugging-contract' into peter/new-timezone-valid…
kongzii Sep 30, 2024
853fe3e
fix bad merge
kongzii Sep 30, 2024
b33ebc0
Merge branch 'peter/new-timezone-validator' into peter/events
kongzii Sep 30, 2024
180902e
Merge branch 'peter/events' into peter/stealing
kongzii Sep 30, 2024
c265c42
use new f
kongzii Sep 30, 2024
2989b8e
remmove unused
kongzii Sep 30, 2024
959e531
Merge branch 'main' into peter/stealing
kongzii Oct 1, 2024
1c7161d
Remove more unsused vars and fix test
kongzii Oct 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import time
from datetime import timedelta

import pytest
from ape_test import TestAccount
from web3 import Web3

from prediction_market_agent_tooling.config import APIKeys
from prediction_market_agent_tooling.gtypes import private_key_type, xdai_type
from prediction_market_agent_tooling.markets.data_models import Resolution
from prediction_market_agent_tooling.markets.omen.data_models import (
OMEN_BINARY_MARKET_OUTCOMES,
)
from prediction_market_agent_tooling.markets.omen.omen import (
OmenAgentMarket,
OmenMarket,
binary_omen_buy_outcome_tx,
binary_omen_sell_outcome_tx,
omen_create_market_tx,
omen_remove_fund_market_tx,
)
from prediction_market_agent_tooling.markets.omen.omen_contracts import (
OMEN_DEFAULT_MARKET_FEE_PERC,
OmenConditionalTokenContract,
WrappedxDaiContract,
)
from prediction_market_agent_tooling.markets.omen.omen_resolving import (
omen_resolve_market_tx,
omen_submit_answer_market_tx,
)
from prediction_market_agent_tooling.tools.balances import get_balances
from prediction_market_agent_tooling.tools.utils import check_not_none, utcnow
from tests.utils import mint_new_block


def test_stealing_on_markets(
accounts: list[TestAccount],
local_web3: Web3,
) -> None:
# Get two accounts, one will create a job market (A) and one will try to sabotage it (B)
account_A, account_B = accounts[0], accounts[1]
api_keys_A, api_keys_B = APIKeys(
BET_FROM_PRIVATE_KEY=private_key_type(account_A.private_key), SAFE_ADDRESS=None
), APIKeys(
BET_FROM_PRIVATE_KEY=private_key_type(account_B.private_key), SAFE_ADDRESS=None
)
print(f"{api_keys_A.bet_from_address=}, {api_keys_B.bet_from_address=}")

# Update chain's state with a dummy block.
mint_new_block(api_keys_A, local_web3)

# Get their starting balances, so we can compare them with their ending balances.
starting_balance_A, starting_balance_B = (
get_balances(api_keys_A.bet_from_address, local_web3).total,
get_balances(api_keys_B.bet_from_address, local_web3).total,
)

# Create the market.
close_in = 10
question = f"Will job X be completed in {close_in} seconds from now?"
created_time = utcnow()
closing_time = created_time + timedelta(seconds=close_in)
funds = xdai_type(10)
finalization_wait_time_seconds = 1
category = "cryptocurrency"
language = "en"
created_market = omen_create_market_tx(
api_keys=api_keys_A,
initial_funds=funds,
fee_perc=OMEN_DEFAULT_MARKET_FEE_PERC,
question=question,
closing_time=closing_time,
category=category,
language=language,
outcomes=OMEN_BINARY_MARKET_OUTCOMES,
finalization_timeout=timedelta(seconds=finalization_wait_time_seconds),
collateral_token_address=WrappedxDaiContract().address,
auto_deposit=True,
web3=local_web3,
)
print(
f"Market created at {created_market.market_event.fixed_product_market_maker_checksummed}"
)

# Initialize OmenMarket and OmenAgentMarket out of it, so we can use it with our standard helper functions.
omen_market = OmenMarket.from_created_market(created_market)
agent_market = OmenAgentMarket.from_data_model(omen_market)
balance_after_market_creation_A = get_balances(
api_keys_A.bet_from_address, local_web3
).total
assert (
balance_after_market_creation_A < starting_balance_A
), "Starting balance of A should have been lowered"

# Buy YES tokens from accout B (attacker) -- removing profit from any real agent that'd like to complete the job.
buy_yes_for_b = xdai_type(5)
binary_omen_buy_outcome_tx(
api_keys_B,
buy_yes_for_b,
agent_market,
binary_outcome=True,
auto_deposit=True,
web3=local_web3,
)
balance_after_buying_A, balance_after_buying_B = (
get_balances(api_keys_A.bet_from_address, local_web3).total,
get_balances(api_keys_B.bet_from_address, local_web3).total,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused variable balance_after_buying_A.

The variable balance_after_buying_A is assigned but never used. Removing it cleans up the code and avoids confusion.

Apply this diff to remove the unused variable:

-    balance_after_buying_A, balance_after_buying_B = (
-        get_balances(api_keys_A.bet_from_address, local_web3).total,
-        get_balances(api_keys_B.bet_from_address, local_web3).total,
-    )
+    balance_after_buying_B = get_balances(api_keys_B.bet_from_address, local_web3).total
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
balance_after_buying_A, balance_after_buying_B = (
get_balances(api_keys_A.bet_from_address, local_web3).total,
get_balances(api_keys_B.bet_from_address, local_web3).total,
)
balance_after_buying_B = get_balances(api_keys_B.bet_from_address, local_web3).total
🧰 Tools
🪛 Ruff

105-105: Local variable balance_after_buying_A is assigned to but never used

Remove assignment to unused variable balance_after_buying_A

(F841)

assert (
balance_after_buying_B < starting_balance_B
), "Balance of B should have be lowered from betting"

# Account A detects this and removes remaining liquidity, so the attacker is locked-in and will loose money unless he completes the job.
omen_remove_fund_market_tx(api_keys_A, agent_market, shares=None, web3=local_web3)
balance_after_removing_funding_A, balance_after_removing_funding_B = (
get_balances(api_keys_A.bet_from_address, local_web3).total,
get_balances(api_keys_B.bet_from_address, local_web3).total,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused variable balance_after_removing_funding_B.

The variable balance_after_removing_funding_B is assigned but never used. Removing it tidies up the code.

Apply this diff to remove the unused variable:

-    balance_after_removing_funding_A, balance_after_removing_funding_B = (
-        get_balances(api_keys_A.bet_from_address, local_web3).total,
-        get_balances(api_keys_B.bet_from_address, local_web3).total,
-    )
+    balance_after_removing_funding_A = get_balances(api_keys_A.bet_from_address, local_web3).total
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
balance_after_removing_funding_A, balance_after_removing_funding_B = (
get_balances(api_keys_A.bet_from_address, local_web3).total,
get_balances(api_keys_B.bet_from_address, local_web3).total,
)
balance_after_removing_funding_A = get_balances(api_keys_A.bet_from_address, local_web3).total
🧰 Tools
🪛 Ruff

115-115: Local variable balance_after_removing_funding_B is assigned to but never used

Remove assignment to unused variable balance_after_removing_funding_B

(F841)

assert (
balance_after_market_creation_A
< balance_after_removing_funding_A
< starting_balance_A
), "Balance after removing the liquidity should be higher than after market creation (because some liquidity can be withdrawn right away), but lower than before market creation (because some liquidity is now locked for the attacker's bet)"

# Buying or selling tokens after the liquidity is removed will fail.
with pytest.raises(Exception) as e_buying:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Unused exception variable e_buying.

The variable e_buying is assigned the exception but not used. If you don't need to inspect the exception, you can omit the as clause.

Apply this diff to remove the unused variable:

-    with pytest.raises(Exception) as e_buying:
+    with pytest.raises(Exception):
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
with pytest.raises(Exception) as e_buying:
with pytest.raises(Exception):
🧰 Tools
🪛 Ruff

126-126: Local variable e_buying is assigned to but never used

Remove assignment to unused variable e_buying

(F841)

binary_omen_buy_outcome_tx(
api_keys_B,
buy_yes_for_b,
agent_market,
binary_outcome=True,
auto_deposit=True,
web3=local_web3,
)
sell_yes_for_b = xdai_type(1)
with pytest.raises(Exception) as e_selling:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Unused exception variable e_selling.

Similarly, e_selling is assigned but not used. You can remove it if it's not needed.

Apply this diff to remove the unused variable:

-    with pytest.raises(Exception) as e_selling:
+    with pytest.raises(Exception):
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
with pytest.raises(Exception) as e_selling:
with pytest.raises(Exception):
🧰 Tools
🪛 Ruff

136-136: Local variable e_selling is assigned to but never used

Remove assignment to unused variable e_selling

(F841)

binary_omen_sell_outcome_tx(
api_keys_B,
sell_yes_for_b,
agent_market,
binary_outcome=True,
auto_withdraw=True,
web3=local_web3,
)
balance_after_failed_trading_B = get_balances(
api_keys_B.bet_from_address, local_web3
).total
assert (
balance_after_failed_trading_B == balance_after_buying_B
), "Balance after failed trading should be the same as after buying of tokens in the beginning, because nothing should have happened."

# Wait for market's closing time
time.sleep(close_in * 1.1)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider mocking time to avoid using time.sleep in tests

Using time.sleep in tests can lead to longer test execution times and potential flakiness. Consider mocking the time or adjusting the contract state to avoid waiting in real time during tests.

Also applies to: 163-163

# Do a dummy block again, so the time in the contract is updated and it knows it's opened already.
mint_new_block(api_keys_A, local_web3)

# Submit answer on reality.
omen_submit_answer_market_tx(
api_keys_A,
omen_market,
Resolution.NO,
bond=xdai_type(0.001),
web3=local_web3,
)

# Wait for the finalization.
time.sleep(finalization_wait_time_seconds * 1.1)
# Update the time in the chain again.
mint_new_block(api_keys_A, local_web3)

# Resolve the market.
omen_resolve_market_tx(api_keys_A, omen_market, local_web3)

# Redeem positions from both accounts.
# Note: Usually we just take all positions from subgraph and redeem them, here we manualy redeem the ones we should have now.
conditional_token_contract = OmenConditionalTokenContract()
condition_event = check_not_none(
created_market.condition_event,
"Should not be None here as this was a freshly created market.",
)
conditional_token_contract.redeemPositions(
api_keys=api_keys_A,
collateral_token_address=agent_market.collateral_token_contract_address_checksummed,
condition_id=condition_event.conditionId,
index_sets=omen_market.condition.index_sets,
web3=local_web3,
)
conditional_token_contract.redeemPositions(
api_keys=api_keys_B,
collateral_token_address=agent_market.collateral_token_contract_address_checksummed,
condition_id=condition_event.conditionId,
index_sets=omen_market.condition.index_sets,
web3=local_web3,
)

# Check who is the winner in the end.
ending_balance_A, ending_balance_B = (
get_balances(api_keys_A.bet_from_address, local_web3).total,
get_balances(api_keys_B.bet_from_address, local_web3).total,
)

assert (
ending_balance_A > starting_balance_A
), "Assumption was that A will receive B's money."
assert (
ending_balance_B < starting_balance_A
), "Assumption was that B will loose the money he gambled by trying to steal from real job completors."
Comment on lines +201 to +203
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix assertion to compare B's ending balance with B's starting balance.

The assertion compares ending_balance_B with starting_balance_A, which is likely incorrect. It should compare ending_balance_B with starting_balance_B to verify that B has lost money.

Apply this diff to correct the assertion and fix the typo in the assertion message:

-    assert (
-        ending_balance_B < starting_balance_A
-    ), "Assumption was that B will loose the money he gambled by trying to steal from real job completors."
+    assert (
+        ending_balance_B < starting_balance_B
+    ), "Assumption was that B will lose the money he gambled by trying to steal from real job completers."
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert (
ending_balance_B < starting_balance_A
), "Assumption was that B will loose the money he gambled by trying to steal from real job completors."
assert (
ending_balance_B < starting_balance_B
), "Assumption was that B will lose the money he gambled by trying to steal from real job completers."


print(
f"Account A (job creator) ending difference: {ending_balance_A - starting_balance_A}."
)
print(
f"Account B (attacker) ending difference: {ending_balance_B - starting_balance_B}."
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests in CI are failing atm, but locally:

clear && pytest tests_integration_with_local_chain -k test_stealing_on_markets -vvv -s

Screenshot by Dropbox Capture

Screenshot by Dropbox Capture

)
Loading