Skip to content

Commit

Permalink
Problem: We are unable to get the required tokens or flow for an inst…
Browse files Browse the repository at this point in the history
…ance or program VM.

Solution: Create a price API to request it just passing the message item hash.
  • Loading branch information
nesitor committed Jan 15, 2024
1 parent 08cd99f commit 5ac4393
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
38 changes: 38 additions & 0 deletions src/aleph/services/cost.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
from aleph.types.files import FileTag


HOUR = 60 * 60
MINUTE = 60


def _get_file_from_ref(
session: DbSession, ref: str, use_latest: bool
) -> Optional[StoredFileDb]:
Expand Down Expand Up @@ -103,3 +107,37 @@ def compute_cost(session: DbSession, content: ExecutableContent) -> Decimal:
)
price = compute_unit_price + get_additional_storage_price(content, session)
return Decimal(price)


def compute_flow_cost(session: DbSession, content: ExecutableContent) -> Decimal:
# TODO: Use PAYMENT_PRICING_AGGREGATE when possible
compute_unit_cost_hour = 0.11 if content.on.persistent else 0.011
compute_unit_cost_second = compute_unit_cost_hour / HOUR

compute_units_required = _get_nb_compute_units(content)
compute_unit_multiplier = _get_compute_unit_multiplier(content)

compute_unit_price = (
Decimal(compute_units_required) * Decimal(compute_unit_multiplier) * Decimal(compute_unit_cost_second)
)

additional_storage_flow_price = get_additional_storage_flow_price(content, session)
price = compute_unit_price + additional_storage_flow_price
return Decimal(price)


def get_additional_storage_flow_price(
content: ExecutableContent, session: DbSession
) -> Decimal:
# TODO: Use PAYMENT_PRICING_AGGREGATE when possible
additional_storage_hour_price = 0.000000977
additional_storage_second_price = Decimal(additional_storage_hour_price) / Decimal(HOUR)
nb_compute_units = _get_nb_compute_units(content)
free_storage_per_compute_unit = 2 * GiB if not content.on.persistent else 20 * GiB

total_volume_size = get_volume_size(session, content)
additional_storage = max(
Decimal(total_volume_size) - (Decimal(free_storage_per_compute_unit) * Decimal(nb_compute_units)), Decimal(0)
)
price = additional_storage / additional_storage_second_price / Decimal(MiB)
return price
54 changes: 54 additions & 0 deletions src/aleph/web/controllers/prices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import logging
from decimal import Decimal
from typing import Optional

from aiohttp import web
from dataclasses import dataclass
from dataclasses_json import DataClassJsonMixin

from aleph_message.models import ItemHash, MessageType
from aleph.db.accessors.messages import (
get_message_status, get_message_by_item_hash,
)
from aleph.services.cost import compute_cost, compute_flow_cost
from aleph.types.db_session import DbSessionFactory

LOGGER = logging.getLogger(__name__)


@dataclass
class MessagePrice(DataClassJsonMixin):
"""Dataclass used to expose message required tokens."""
required_tokens: Optional[Decimal] = None


async def message_price(request: web.Request):
item_hash_str = request.match_info.get("item_hash")
try:
item_hash = ItemHash(item_hash_str)
except ValueError:
raise web.HTTPUnprocessableEntity(body=f"Invalid message hash: {item_hash_str}")

session_factory: DbSessionFactory = request.app["session_factory"]
with session_factory() as session:
message_status_db = get_message_status(session=session, item_hash=item_hash)
if message_status_db is None:
raise web.HTTPNotFound()
message = get_message_by_item_hash(session, item_hash)

if not message or message.type == (not MessageType.instance or not MessageType.program):
raise web.HTTPUnprocessableEntity(
body=f"Invalid message hash: {item_hash_str}"
)

content = message.parsed_content

if content.payment and content.payment.is_stream:
required_tokens = compute_flow_cost(session=session, content=content)
else:
required_tokens = compute_cost(session=session, content=content)

return web.Response(
text=MessagePrice(required_tokens=required_tokens).to_json(),
content_type="application/json",
)
3 changes: 3 additions & 0 deletions src/aleph/web/controllers/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
messages,
p2p,
posts,
prices,
storage,
version,
)
Expand Down Expand Up @@ -57,6 +58,8 @@ def register_routes(app: web.Application):
app.router.add_get("/api/v1/posts.json", posts.view_posts_list_v1)
app.router.add_get("/api/v1/posts/page/{page}.json", posts.view_posts_list_v1)

app.router.add_get("/api/v0/price/{item_hash}", prices.message_price)

app.router.add_get("/api/v0/addresses/stats.json", accounts.addresses_stats_view)
app.router.add_get(
"/api/v0/addresses/{address}/balance", accounts.get_account_balance
Expand Down

0 comments on commit 5ac4393

Please sign in to comment.