Skip to content
This repository has been archived by the owner on Oct 31, 2023. It is now read-only.

Task API RequestorMarketStrategy fetch in VerificationMixin #4831

Merged
merged 4 commits into from
Oct 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions golem/marketplace/brass_marketplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,13 @@ def resolve_task_offers(cls, task_id: str) -> Optional[List[Offer]]:
@classmethod
def get_payment_computer(
cls,
task: 'Task',
subtask_id: str
subtask_id: str,
subtask_timeout: int,
subtask_price: int,
) -> Callable[[int], int]:

def payment_computer(price: int):
return compute_subtask_value(price, task.header.subtask_timeout)
return compute_subtask_value(price, subtask_timeout)

return payment_computer

Expand Down
8 changes: 6 additions & 2 deletions golem/marketplace/marketplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,12 @@ def get_task_offer_count(cls, task_id: str) -> int:
raise NotImplementedError()

@abstractclassmethod
def get_payment_computer(cls, task: 'Task', subtask_id: str)\
-> Callable[[int], int]:
def get_payment_computer(
cls,
subtask_id: str,
subtask_timeout: int,
subtask_price: int,
) -> Callable[[int], int]:
"""Returns a function computing payment based on price in TTC.
Raises:
NotImplementedError: [description]
Expand Down
8 changes: 5 additions & 3 deletions golem/marketplace/wasm_marketplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,14 @@ def _get_subtask_usage(cls, subtask_id: str) -> float:

@classmethod
def get_payment_computer(
cls, task: 'Task',
subtask_id: str
cls,
subtask_id: str,
subtask_timeout: int,
subtask_price: int,
) -> Callable[[int], int]:
def payment_computer(price: int) -> int:
subtask_usage: float = cls._get_subtask_usage(subtask_id)
return min(int(price * subtask_usage / 3600), task.subtask_price)
return min(int(price * subtask_usage / 3600), subtask_price)

return payment_computer

Expand Down
57 changes: 41 additions & 16 deletions golem/task/server/verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
if typing.TYPE_CHECKING:
# pylint: disable=unused-import
from golem.core import keysauth
from golem.task import taskmanager
from golem.task import taskmanager, SubtaskId, TaskId
from golem.task import requestedtaskmanager

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -84,21 +84,7 @@ def verification_finished(
timeout_seconds=config_desc.disallow_ip_timeout_seconds,
)

task = self.task_manager.tasks.get(task_id)
if task:
strat = task.REQUESTOR_MARKET_STRATEGY
payment_computer = strat.get_payment_computer( # type: ignore
task,
subtask_id)
else:
# FIXME: adjust after merging #4753 (with #4785)
def payment_computer(price: int):
return price * subtask_timeout

task = self.requested_task_manager.get_requested_task(task_id)
assert task, "Completed verification for an unknown task"
subtask_timeout = task.subtask_timeout

payment_computer = self._get_payment_computer(task_id, subtask_id)
payment = self.accept_result(
task_id,
subtask_id,
Expand Down Expand Up @@ -158,6 +144,45 @@ def verification_finished_old():
verification_finished_old,
)

def _get_payment_computer(
self,
task_id: 'TaskId',
subtask_id: 'SubtaskId',
) -> typing.Callable[[int], int]:
""" Retrieve the payment computing function for given
task_id and subtask_id """
task = self.task_manager.tasks.get(task_id)
if task:
market = task.REQUESTOR_MARKET_STRATEGY
return market.get_payment_computer( # type: ignore
subtask_id,
subtask_timeout=int(task.header.subtask_timeout),
subtask_price=task.subtask_price)

task = self.requested_task_manager.get_requested_task(task_id)
if not task:
raise RuntimeError(
f"Completed verification of subtask {subtask_id} "
f"within an unknown task {task_id}")

subtask = self.requested_task_manager.get_requested_task_subtask(
task_id,
subtask_id)
if not subtask:
raise RuntimeError(
f"Completed verification of unknown subtask {subtask_id} "
f"within task {task_id}")

app = self.app_manager.app(task.app_id)
if not app:
raise RuntimeError(
f"Completed verification of task {task_id} "
f"created by an unknown app {task.app_id}")
return app.market_strategy.get_payment_computer(
subtask_id,
subtask_timeout=task.subtask_timeout,
subtask_price=subtask.price)

def send_result_rejected(
self,
report_computed_task: message.tasks.ReportComputedTask,
Expand Down
2 changes: 1 addition & 1 deletion golem/task/taskbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def price(self) -> int:
return self.subtask_price * self.get_total_tasks()

@property
def subtask_price(self):
def subtask_price(self) -> int:
from golem.task import taskkeeper
return taskkeeper.compute_subtask_value(
self.header.max_price,
Expand Down
33 changes: 17 additions & 16 deletions tests/golem/marketplace/test_marketplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,7 @@


def _fake_get_efficacy():

class A:

def __init__(self):
self.vector = (.0, .0, .0, .0)

return A()
return Mock(vector=(.0, .0, .0, .0))


class TestScalePrice(TestCase):
Expand Down Expand Up @@ -52,7 +46,10 @@ def test_brass_payment_computer(self):
task = Mock()
task.header = Mock()
task.header.subtask_timeout = 360
payment_computer = market_strategy.get_payment_computer(task, None)
payment_computer = market_strategy.get_payment_computer(
None,
task.header.subtask_timeout,
task.subtask_price)
self.assertEqual(payment_computer(100), 10) # price * timeout / 3600

def test_wasm_payment_computer(self):
Expand All @@ -66,13 +63,15 @@ def test_wasm_payment_computer(self):
(self.PROVIDER_B, self.SUBTASK_B, 8.0 * HOUR)]
)
payment_computer = market_strategy.get_payment_computer(
task, self.SUBTASK_A
)
self.SUBTASK_A,
task.header.subtask_timeout,
task.subtask_price)
self.assertEqual(payment_computer(1000 * GWEI), 5000 * GWEI)

payment_computer = market_strategy.get_payment_computer(
task, self.SUBTASK_B
)
self.SUBTASK_B,
task.header.subtask_timeout,
task.subtask_price)
self.assertEqual(payment_computer(1000 * GWEI), 6000 * GWEI)

def test_wasm_payment_computer_budget_exceeded(self):
Expand All @@ -86,13 +85,15 @@ def test_wasm_payment_computer_budget_exceeded(self):
(self.PROVIDER_B, self.SUBTASK_B, 8.0 * HOUR)]
)
payment_computer = market_strategy.get_payment_computer(
task, self.SUBTASK_A
)
self.SUBTASK_A,
task.header.subtask_timeout,
task.subtask_price)
self.assertEqual(payment_computer(1000 * GWEI), 5000 * GWEI)

payment_computer = market_strategy.get_payment_computer(
task, self.SUBTASK_B
)
self.SUBTASK_B,
task.header.subtask_timeout,
task.subtask_price)
self.assertEqual(payment_computer(1000 * GWEI), 6000 * GWEI)


Expand Down
83 changes: 83 additions & 0 deletions tests/golem/task/server/test_verification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import unittest
from unittest import mock

from golem.task.server.verification import VerificationMixin


class TestGetPaymentComputer(unittest.TestCase):

def setUp(self) -> None:
self.market_legacy = mock.Mock()
self.market_task_api = mock.Mock()
self.apps = {
'app_id': mock.Mock(market_strategy=self.market_task_api)
}
tasks = {
'legacy_task_id': mock.Mock(
REQUESTOR_MARKET_STRATEGY=self.market_legacy,
header=mock.Mock(subtask_timeout=3600),
subtask_price=10 ** 18,
)
}
task_api_tasks = {
'task_api_task_id': mock.Mock(
app_id='app_id',
subtask_timeout=1800,
)
}
task_api_subtasks = {
'subtask_id': mock.Mock(price=10 ** 17),
}

am = mock.Mock(app=self.apps.get)
tm = mock.Mock(tasks=tasks)
rtm = mock.Mock(
get_requested_task=task_api_tasks.get,
get_requested_task_subtask=mock.Mock(
side_effect=(
lambda t, s: task_api_subtasks.get(s)
if t in task_api_tasks else None
),
)
)

self.verifier = VerificationMixin()
self.verifier.app_manager = am
self.verifier.task_manager = tm
self.verifier.requested_task_manager = rtm
self.get = self.verifier._get_payment_computer

def test_legacy(self):
self.get(
task_id='legacy_task_id',
subtask_id='subtask_id')

self.assertFalse(self.market_task_api.get_payment_computer.called)
self.market_legacy.get_payment_computer.assert_called_with(
'subtask_id',
subtask_timeout=3600,
subtask_price=10 ** 18)

def test_task_api(self):
self.get(
task_id='task_api_task_id',
subtask_id='subtask_id')

self.assertFalse(self.market_legacy.get_payment_computer.called)
self.market_task_api.get_payment_computer.assert_called_with(
'subtask_id',
subtask_timeout=1800,
subtask_price=10 ** 17)

def test_task_id_invalid(self):
with self.assertRaisesRegex(RuntimeError, "unknown task"):
self.get(task_id='UNKNOWN', subtask_id='subtask_id')

def test_subtask_id_invalid(self):
with self.assertRaisesRegex(RuntimeError, "unknown subtask"):
self.get(task_id='task_api_task_id', subtask_id='UNKNOWN')

def test_app_id_invalid(self):
self.apps.clear()
with self.assertRaisesRegex(RuntimeError, "unknown app"):
self.get(task_id='task_api_task_id', subtask_id='subtask_id')