Skip to content

Commit

Permalink
Merge pull request #103 from valory-xyz/fix/is_profitable
Browse files Browse the repository at this point in the history
Fix/is profitable
  • Loading branch information
DavidMinarsch authored Sep 2, 2022
2 parents 5a0c12c + 85d998a commit 89ba928
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 237 deletions.
8 changes: 4 additions & 4 deletions packages/hashes.csv
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
gabrielfu/contracts/keep3r_job,bafybeihgz7flasaijeachrkmpgyqdqmeawknh2nfoie2page57ejy5l4ai
keep3r_co/agents/keep3r_bot,bafybeic6g43k3x7lfcxfz4kqdu54f3x6l32l6b46ugwfd6h4orzkjmioam
keep3r_co/skills/keep3r_abci,bafybeihgocdrvix4qwekwqlgseketai5g7sxk6zcz6n36i6navyynztmvu
keep3r_co/skills/keep3r_job,bafybeidjoqdpwisblrb7pi76sjjwum3rgznvdssug4jgdzmvguxkv5jybq
keep3r_co/agents/keep3r_bot,bafybeihoauct2di33nwf5l4ivulqbdvriz3rgsk5v2552a4wubj5fo5zvm
keep3r_co/skills/keep3r_abci,bafybeigpmhu37mx7sjz4fnphanzq42tyhxu6qtouxux3kz7r7fqgh5idsu
keep3r_co/skills/keep3r_job,bafybeidwqelqqvps6eiqnfmnxaeevkr7qyjlpke4q5undblc64ajycbhn4
open_aea/connections/scaffold,bafybeifucsl5zfe4if4zfaddud77lzwgg7emv3mr3x3us57py7vbhktb24
open_aea/contracts/scaffold,bafybeiba4pdufnxve5hdkla42lixnxwfkyg7s2bvuy6zqbxjsxtaygehda
open_aea/protocols/scaffold,bafybeihhyjuqvp5zqtikkjrwtikg4fr4tsub43n777huxtpeu77g75poze
Expand All @@ -14,7 +14,7 @@ valory/connections/p2p_libp2p_client,bafybeigvayl4ykzqf6o6bw2irv7am3qvczjoeu7yjh
valory/contracts/gnosis_safe,bafybeif5ump4p7p5bygqyxeznwdvtdhtwihxnjnvjajbcnt7rmcyzyjafy
valory/contracts/gnosis_safe_proxy_factory,bafybeiffdg3dpuynvbw7vusdiuuqkgl4gxok6ezlgv3senji2ybnl6m2iy
valory/contracts/keep3r_test_job,bafybeid4k344cidfr5f7d5xxdlygzqdrgqdfauhsdfti3uvky3d5pxko5a
valory/contracts/keep3r_v1,bafybeibkeo5r4i2lopjk4vo6lz3rdlghmrf5nb554t5h3e7qn3ne4xt6ny
valory/contracts/keep3r_v1,bafybeiab6raob7r47absd7zh7fkulyhpcogzf3xliuicmxusqqfj2eeera
valory/contracts/keep3r_v1_library,bafybeid2h244yjm6iledd5vxmq37y33cczkzvkdfo3nxkucf6sfopf22fa
valory/contracts/multisend,bafybeidvuabdn32lpsh3z7wb5fonotimysfsntueeb53dgil5jmiezrasq
valory/contracts/service_registry,bafybeidtwbj7mwiskbbmsw43zzooivuq6nripdzn7yuoou66rq7spom54m
Expand Down
4 changes: 2 additions & 2 deletions packages/keep3r_co/agents/keep3r_bot/aea-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ protocols:
- valory/ledger_api:1.0.0:bafybeif2c3jt4tim3zbfq5c2lds7bs6qj5gvj2obk3pzdk3uy6zk2ivo3e
- valory/tendermint:0.1.0:bafybeihcnjhovvyyfbkuw5sjyfx2lfd4soeocfqzxz54g67333m6nk5gxq
skills:
- keep3r_co/keep3r_abci:0.1.0:bafybeihgocdrvix4qwekwqlgseketai5g7sxk6zcz6n36i6navyynztmvu
- keep3r_co/keep3r_job:0.1.0:bafybeidjoqdpwisblrb7pi76sjjwum3rgznvdssug4jgdzmvguxkv5jybq
- keep3r_co/keep3r_abci:0.1.0:bafybeigpmhu37mx7sjz4fnphanzq42tyhxu6qtouxux3kz7r7fqgh5idsu
- keep3r_co/keep3r_job:0.1.0:bafybeidwqelqqvps6eiqnfmnxaeevkr7qyjlpke4q5undblc64ajycbhn4
- valory/abstract_abci:0.1.0:bafybeiam52ztcss46i3tgywuv4qmvddqgn7exmbt5kcicqpyip4kfeyap4
- valory/abstract_round_abci:0.1.0:bafybeiayewfb2ytovp7exhawqwbw63eerx4tw53ztey5r4ozbtcvsw3gua
- valory/registration_abci:0.1.0:bafybeibe2so7dcm2sivhum7gqik27c73a6v7gf3aligmbbucgh2zh2wvzu
Expand Down
2 changes: 1 addition & 1 deletion packages/keep3r_co/skills/keep3r_abci/skill.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ connections: []
contracts: []
protocols: []
skills:
- keep3r_co/keep3r_job:0.1.0:bafybeidjoqdpwisblrb7pi76sjjwum3rgznvdssug4jgdzmvguxkv5jybq
- keep3r_co/keep3r_job:0.1.0:bafybeidwqelqqvps6eiqnfmnxaeevkr7qyjlpke4q5undblc64ajycbhn4
- valory/abstract_round_abci:0.1.0:bafybeiayewfb2ytovp7exhawqwbw63eerx4tw53ztey5r4ozbtcvsw3gua
- valory/registration_abci:0.1.0:bafybeibe2so7dcm2sivhum7gqik27c73a6v7gf3aligmbbucgh2zh2wvzu
- valory/reset_pause_abci:0.1.0:bafybeiehyoay5bjg7zpc47bynycvs7gf7o7tcau77b5rwsgpg7shnrjnje
Expand Down
43 changes: 6 additions & 37 deletions packages/keep3r_co/skills/keep3r_job/behaviours.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,52 +361,21 @@ class IsProfitableBehaviour(Keep3rJobBaseBehaviour):
matching_round: Type[AbstractRound] = IsProfitableRound

def async_act(self) -> Generator:
"""Do the action
Steps:
- Call the contract to get the rewardMultiplier
- Check if the job is profitable given the current rewardMultiplier
- Set Payload accordingly and send transaction, then end the round.
"""
"""Do the act, supporting asynchronous execution."""

with self.context.benchmark_tool.measure(self.behaviour_id).local():
reward_multiplier = yield from self.rewardMultiplier()
if reward_multiplier is None:
raise RuntimeError("Contract call has failed")

# TODO: compute a more meaningful profitability measure
if reward_multiplier > self.context.params.profitability_threshold:
payload = IsProfitablePayload(self.context.agent_address, True)
else:
payload = IsProfitablePayload(self.context.agent_address, False)
current_job = self.synchronized_data.current_job
reward = yield from self.read_keep3r_v1("credits", address=current_job)
is_profitable = reward >= self.context.params.profitability_threshold
self.context.logger.info(f"reward: {reward}, profitable: {is_profitable}")
payload = IsProfitablePayload(self.context.agent_address, is_profitable)

with self.context.benchmark_tool.measure(self.behaviour_id).consensus():
self.context.logger.info(f"Safe transaction hash: {reward_multiplier}")
yield from self.send_a2a_transaction(payload)
yield from self.wait_until_round_end()

self.set_done()

def rewardMultiplier(self) -> Generator:
"""Calls the contract to get the reward multiplier for the job."""

contract_api_response = yield from self.get_contract_api_response(
performative=ContractApiMessage.Performative.GET_STATE,
contract_address=self.synchronized_data.current_job,
contract_id=str(Keep3rTestJobContract.contract_id),
contract_callable="rewardMultiplier",
)
if (
contract_api_response.performative != ContractApiMessage.Performative.STATE
): # pragma: nocover
self.context.logger.warning("Get reward multiplier unsuccessful!")
return None

reward_multiplier = cast(
int, contract_api_response.state.body.pop("rewardMultiplier")
)
return reward_multiplier


class PerformWorkBehaviour(Keep3rJobBaseBehaviour):
"""PerformWorkBehaviour"""
Expand Down
21 changes: 12 additions & 9 deletions packages/keep3r_co/skills/keep3r_job/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,21 @@ def setup(self) -> None:
class Params(BaseParams):
"""Parameters."""

required = [
"validate_timeout",
"finalize_timeout",
"job_contract_addresses",
"keep3r_v1_contract_address",
"insufficient_funds_threshold",
"profitability_threshold",
]

def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Initialize the parameters object."""
self.validate_timeout = self._ensure("validate_timeout", kwargs)
self.finalize_timeout = self._ensure("finalize_timeout", kwargs)
self.job_contract_addresses = self._ensure("job_contract_addresses", kwargs)
self.keep3r_v1_contract_address = self._ensure(
"keep3r_v1_contract_address", kwargs
)
self.insufficient_funds_threshold = self._ensure(
"insufficient_funds_threshold", kwargs
)

super().__init__(*args, **kwargs)
for item in self.required:
setattr(self, item, self._ensure(item, kwargs))


class RandomnessApi(ApiSpecs):
Expand Down
7 changes: 3 additions & 4 deletions packages/keep3r_co/skills/keep3r_job/rounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,11 +283,10 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]:
"""Process the end of the block."""

if self.threshold_reached:
state = self.synchronized_data.update(is_profitable=self.most_voted_payload)
is_profitable = self.most_voted_payload
if is_profitable:
return state, Event.PROFITABLE
return state, Event.NOT_PROFITABLE
state = self.synchronized_data.update(is_profitable=is_profitable)
return state, Event.PROFITABLE if is_profitable else Event.NOT_PROFITABLE

if not self.is_majority_possible(
self.collection, self.synchronized_data.nb_participants
):
Expand Down
10 changes: 5 additions & 5 deletions packages/keep3r_co/skills/keep3r_job/skill.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ aea_version: '>=1.0.0, <2.0.0'
fingerprint:
README.md: bafybeidq32yfua6bopvzlo7xwpfdiz4bwr7txkv4vo4vxmjmvdthkr2cwe
__init__.py: bafybeiewrlbkuksvnjfs5aubeg3ubwyxcqm3qytv2svbvk2lq7k36zlj2y
behaviours.py: bafybeigm3okkgvih5k5gnikyg7a75vljthqf6evthwlshc7xgevz6vui5a
behaviours.py: bafybeiddtejmvgnxl2id6dj2d6iafwvcnqcd5dfmftu25l2royvjz3kufu
dialogues.py: bafybeib4jbdycm2vofni3vp6draqet3nejoviqjurcilrzindr4k5dg5fa
fsm_specification.yaml: bafybeifjaklbcefmwqkr45ar7ak3fkhodb73ulddamwot7lymqbrc55j4q
handlers.py: bafybeie6ntjlqfb5glkciuo56pisvgobi2tuqhcjlzm7h5uvhgi2ipxrzq
models.py: bafybeifxjxhec2vsi7oitjxqdzsgou5w4bg7addhzu7zttjokgncqkiayu
models.py: bafybeifs66ic4uzwrgkzmbvzzg42ugees4dejv5n7nioppljawrywtirvu
payloads.py: bafybeicpmqifm4fnccqhaeprjnj3azxj5b5shtnf7ybomfrysnuh4n6yoe
rounds.py: bafybeigh7jqgpe6fo3dq22jbyanwk6yk6wt3wssn6zmvm7dibxq7ik5xgy
rounds.py: bafybeih2g3kcfgpis3xasjh4uliw7dd2ypqumv6ipptd25eg2gexotja3y
fingerprint_ignore_patterns: []
connections: []
contracts:
- valory/gnosis_safe:0.1.0:bafybeif5ump4p7p5bygqyxeznwdvtdhtwihxnjnvjajbcnt7rmcyzyjafy
- valory/keep3r_test_job:0.1.0:bafybeid4k344cidfr5f7d5xxdlygzqdrgqdfauhsdfti3uvky3d5pxko5a
- valory/keep3r_v1:0.1.0:bafybeibkeo5r4i2lopjk4vo6lz3rdlghmrf5nb554t5h3e7qn3ne4xt6ny
- valory/keep3r_v1:0.1.0:bafybeiab6raob7r47absd7zh7fkulyhpcogzf3xliuicmxusqqfj2eeera
protocols:
- valory/contract_api:1.0.0:bafybeigkedpmqhm7lrqusnkyfcapyx3dlkz2djfq74hou5pwtrskxgvuxu
- valory/ledger_api:1.0.0:bafybeif2c3jt4tim3zbfq5c2lds7bs6qj5gvj2obk3pzdk3uy6zk2ivo3e
Expand Down Expand Up @@ -83,7 +83,7 @@ models:
max_healthcheck: 120
observation_interval: 10
on_chain_service_id: null
profitability_threshold: 500
profitability_threshold: 0
reset_tendermint_after: 2
retry_attempts: 400
retry_timeout: 3
Expand Down
12 changes: 12 additions & 0 deletions packages/valory/contracts/keep3r_v1/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ def blacklist(
contract = cls.get_instance(ledger_api, contract_address)
return contract.functions.blacklist(address).call()

@classmethod
def credits(
cls,
ledger_api: EthereumApi,
contract_address: str,
address: str,
) -> int:
"""Check current credit available for a job"""

contract = cls.get_instance(ledger_api, contract_address)
return contract.functions.credits(address, contract.address).call()

@classmethod
def get_jobs(
cls,
Expand Down
2 changes: 1 addition & 1 deletion packages/valory/contracts/keep3r_v1/contract.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ aea_version: '>=1.0.0, <2.0.0'
fingerprint:
__init__.py: bafybeife7pjewbl5fi6b2v4hw6jflxi2jbav77odmllecr6n7e5qg7go6q
build/Keep3rV1.json: bafybeiaogvk56d5mpnizla2klyg67clcp7wf2haqbqs4avf5lopavxhkya
contract.py: bafybeibhrvmldckyehl4dvgqvgbatnhpqw327xzlzmgwksck4zwdrl46f4
contract.py: bafybeiez37ahbapax5yyhthks2a3ylsr33d6rpcjqxzk3o5a55xbrewkom
fingerprint_ignore_patterns: []
contracts:
- valory/keep3r_v1_library:0.1.0:bafybeid2h244yjm6iledd5vxmq37y33cczkzvkdfo3nxkucf6sfopf22fa
Expand Down
6 changes: 6 additions & 0 deletions tests/test_contracts/test_keep3r_v1/test_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ def test_blacklist(self) -> None:
kw = dict(address=self.deployer_crypto.address)
assert self.contract.blacklist(**self.base_kw, **kw) is False

def test_credits(self) -> None:
"""Test credits"""

kw = dict(address=self.deployer_crypto.address)
assert self.contract.credits(**self.base_kw, **kw) == 0

def test_get_jobs(self) -> None:
"""Test get_jobs"""

Expand Down
100 changes: 18 additions & 82 deletions tests/test_skills/test_keep3r_job/test_behaviours.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,6 @@
AwaitTopUpRound,
BlacklistedRound,
BondingRound,
)
from packages.keep3r_co.skills.keep3r_job.rounds import (
DegenerateRound as NothingToDoRound,
)
from packages.keep3r_co.skills.keep3r_job.rounds import (
Event,
FinalizeActivationRound,
FinalizeBondingRound,
Expand All @@ -70,11 +65,10 @@
JobSelectionRound,
Keep3rJobAbstractRound,
PathSelectionRound,
PerformWorkRound,
SynchronizedData,
WaitingRound,
)
from packages.keep3r_co.skills.keep3r_job.rounds import (
PerformWorkRound as PrepareTxRound,
)
from packages.keep3r_co.skills.keep3r_job.rounds import SynchronizedData, WaitingRound
from packages.valory.contracts.gnosis_safe.contract import (
PUBLIC_ID as GNOSIS_SAFE_CONTRACT_ID,
)
Expand Down Expand Up @@ -473,84 +467,26 @@ def test_is_workable(
assert self.current_behaviour.behaviour_id == next_round.round_id


@pytest.mark.skip("ABCIApp redesign: no payment assigned yet")
class TestIsProfitableBehaviour(Keep3rJobFSMBehaviourBaseCase):
"""Test case to test IsProfitableBehaviour."""

CONTRACT_ADDRESS: str = "contract_address"
CONTRACT_CALLABLE: str = "rewardMultiplier"
is_profitable_behaviour_class: Type[BaseBehaviour] = IsProfitableBehaviour

def test_is_profitable_true(self) -> None:
"""Test is profitable true."""
self.skill.skill_context.params.job_contract_addresses = ["job_contract_1"]
self.fast_forward_to_behaviour(
self.behaviour,
self.is_profitable_behaviour_class.behaviour_id,
SynchronizedData(
AbciAppDB(
setup_data=AbciAppDB.data_to_lists(dict(job_selection="some_job"))
)
),
)

assert self.current_behaviour.behaviour_id == IsProfitableBehaviour.behaviour_id

self.behaviour.context.params.profitability_threshold = 100
self.behaviour.act_wrapper()
self.mock_contract_api_request(
contract_id=str(TEST_JOB_CONTRACT_ID),
request_kwargs=dict(
performative=ContractApiMessage.Performative.GET_STATE,
callable=self.CONTRACT_CALLABLE,
),
response_kwargs=dict(
performative=ContractApiMessage.Performative.STATE,
callable=self.CONTRACT_CALLABLE,
state=ContractApiMessage.State(
ledger_id="ethereum",
body={"rewardMultiplier": 90},
),
),
)
self.mock_a2a_transaction()
self._test_done_flag_set()
self.end_round(done_event=Event.DONE)
assert self.current_behaviour.behaviour_id == PrepareTxRound.round_id
behaviour_class: Type[BaseBehaviour] = IsProfitableBehaviour

def test_is_profitable_false(self) -> None:
"""Test is profitable false."""
self.skill.skill_context.params.job_contract_addresses = ["job_contract_1"]
self.fast_forward_to_behaviour(
self.behaviour,
self.is_profitable_behaviour_class.behaviour_id,
SynchronizedData(
AbciAppDB(
setup_data=AbciAppDB.data_to_lists(dict(job_selection="some_job"))
)
),
)
assert self.current_behaviour.behaviour_id == IsProfitableBehaviour.behaviour_id
@pytest.mark.parametrize(
"credits, event, next_round",
[
(-1, Event.NOT_PROFITABLE, JobSelectionRound),
(1, Event.PROFITABLE, PerformWorkRound),
],
)
def test_is_profitable(
self, credits: bool, event: Event, next_round: Keep3rJobAbstractRound
) -> None:
"""Test is_profitable."""

self.behaviour.context.params.profitability_threshold = 100
self.mock_keep3r_v1_call("credits", credits)
self.behaviour.act_wrapper()
self.mock_contract_api_request(
contract_id=str(TEST_JOB_CONTRACT_ID),
request_kwargs=dict(
performative=ContractApiMessage.Performative.GET_STATE,
callable=self.CONTRACT_CALLABLE,
),
response_kwargs=dict(
performative=ContractApiMessage.Performative.STATE,
callable=self.CONTRACT_CALLABLE,
state=ContractApiMessage.State(
ledger_id="ethereum",
body={"rewardMultiplier": 110},
),
),
)
self.mock_a2a_transaction()
self._test_done_flag_set()
self.end_round(done_event=Event.NOT_PROFITABLE)
degenerate_state = make_degenerate_behaviour(NothingToDoRound.round_id)
assert self.current_behaviour.behaviour_id == degenerate_state.behaviour_id
self.end_round(done_event=event)
assert self.current_behaviour.behaviour_id == next_round.round_id
Loading

0 comments on commit 89ba928

Please sign in to comment.