按显卡世代和创世编号结算回本,机房批量接入需审核网段并用硬件指纹防克隆。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,9 +7,10 @@
|
||||
3. 任务响应率 = 校验通过且执行成功的任务数 / 当日内下发任务数。
|
||||
4. 当天没有下发任务时,表现系数等于在线率,避免调度器故障误伤节点。
|
||||
有任务时,表现系数 = 在线权重 × 在线率 + 任务权重 × 响应率。
|
||||
5. 算力产出 = 授权金额 × 表现系数。
|
||||
当日积分 = 算力产出 / 授权天数。
|
||||
6. 单节点累计积分不超过授权金额。已存在的当日流水不会被覆盖。
|
||||
5. 显卡世代决定回本天数:40 系及以上 210 天,30 系 365 天,10 系及以下或核显 540 天。
|
||||
每日基础释放 = 购买金额 / 回本天数。
|
||||
当日积分 = 每日基础释放 × 表现系数。表现系数就是当日平台收益加权系数。
|
||||
6. 积分优先冲减 recoverable_stake_amount,直到质押金返还完毕。已存在的当日流水不会被覆盖。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -28,6 +29,7 @@ from app.errors import AppError
|
||||
from app.models import ComputeNode, ComputeTask, NodeYield, TaskStatus, UptimeInterval, YieldStatus
|
||||
from app.money import money, rate, ratio, to_decimal
|
||||
from app.services.common import get_node_by_sn
|
||||
from app.services.hardware import DEFAULT_WEIGHT, RECOVERY_10_DAYS
|
||||
from app.services.tasks import expire_overdue_tasks
|
||||
|
||||
|
||||
@@ -47,6 +49,9 @@ class YieldComputation:
|
||||
compute_output: Decimal = Decimal("0")
|
||||
points_delta: Decimal = Decimal("0")
|
||||
cycle_days: int = 0
|
||||
base_release: Decimal = Decimal("0")
|
||||
hardware_weight: Decimal = Decimal("0")
|
||||
remaining_points: Decimal = Decimal("0")
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -68,9 +73,9 @@ def compute_node_yield(
|
||||
day_start, day_end = day_bounds(yield_date)
|
||||
window_start = max(day_start, node.licensed_at)
|
||||
window_end = min(day_end, node.license_expires_at)
|
||||
cycle_days = (node.license_expires_at.date() - node.licensed_at.date()).days
|
||||
if window_end <= window_start or cycle_days <= 0:
|
||||
return YieldComputation(eligible=False, yield_date=yield_date, cycle_days=max(cycle_days, 0))
|
||||
recovery_days = int(node.recovery_days or RECOVERY_10_DAYS)
|
||||
if window_end <= window_start or recovery_days <= 0:
|
||||
return YieldComputation(eligible=False, yield_date=yield_date, cycle_days=max(recovery_days, 0))
|
||||
|
||||
eligible_seconds = int((window_end - window_start).total_seconds())
|
||||
online_seconds = min(_online_seconds(session, node, window_start, window_end, moment), eligible_seconds)
|
||||
@@ -94,9 +99,11 @@ def compute_node_yield(
|
||||
applied_online = to_decimal(settings.online_weight)
|
||||
applied_task = to_decimal(settings.task_weight)
|
||||
performance = rate(applied_online * online_rate + applied_task * response_rate)
|
||||
base_release = to_decimal(node.purchase_amount) / Decimal(recovery_days)
|
||||
hardware_weight = to_decimal(node.hardware_weight if node.hardware_weight is not None else DEFAULT_WEIGHT)
|
||||
remaining = _remaining_stake(session, node)
|
||||
points_delta = min(money(base_release * performance), remaining)
|
||||
compute_output = money(to_decimal(node.purchase_amount) * performance)
|
||||
points_delta = money(compute_output / Decimal(cycle_days))
|
||||
points_delta = min(points_delta, _remaining_points(session, node))
|
||||
|
||||
return YieldComputation(
|
||||
eligible=True,
|
||||
@@ -112,16 +119,20 @@ def compute_node_yield(
|
||||
performance=performance,
|
||||
compute_output=compute_output,
|
||||
points_delta=points_delta,
|
||||
cycle_days=cycle_days,
|
||||
cycle_days=recovery_days,
|
||||
base_release=base_release,
|
||||
hardware_weight=hardware_weight,
|
||||
remaining_points=remaining,
|
||||
)
|
||||
|
||||
|
||||
def settle_yield_date(session: Session, yield_date: date, now: datetime | None = None) -> SettleSummary:
|
||||
"""清算指定 UTC 日。每个节点当天已有流水时跳过,保证脚本重复执行不会重复入账。"""
|
||||
def network_yields(
|
||||
session: Session,
|
||||
yield_date: date,
|
||||
now: datetime | None = None,
|
||||
) -> list[tuple[ComputeNode, YieldComputation]]:
|
||||
"""算出授权覆盖该日的全部节点。积分按各节点自己的显卡世代释放。"""
|
||||
moment = now or utcnow()
|
||||
if yield_date > moment.date():
|
||||
raise AppError("不能清算未来日期")
|
||||
expire_overdue_tasks(session, moment)
|
||||
day_start, day_end = day_bounds(yield_date)
|
||||
nodes = session.scalars(
|
||||
select(ComputeNode)
|
||||
@@ -131,23 +142,32 @@ def settle_yield_date(session: Session, yield_date: date, now: datetime | None =
|
||||
)
|
||||
.order_by(ComputeNode.id)
|
||||
).all()
|
||||
return [(node, compute_node_yield(session, node, yield_date, moment)) for node in nodes]
|
||||
|
||||
|
||||
def settle_yield_date(session: Session, yield_date: date, now: datetime | None = None) -> SettleSummary:
|
||||
"""清算指定 UTC 日。每个节点当天已有流水时跳过,保证脚本重复执行不会重复入账。"""
|
||||
moment = now or utcnow()
|
||||
if yield_date > moment.date():
|
||||
raise AppError("不能清算未来日期")
|
||||
expire_overdue_tasks(session, moment)
|
||||
created = 0
|
||||
skipped = 0
|
||||
total = Decimal("0")
|
||||
partial = yield_date == moment.date()
|
||||
for node in nodes:
|
||||
for node, computed in network_yields(session, yield_date, moment):
|
||||
exists = session.scalar(
|
||||
select(NodeYield.id).where(NodeYield.node_id == node.id, NodeYield.yield_date == yield_date)
|
||||
)
|
||||
if exists is not None:
|
||||
skipped += 1
|
||||
continue
|
||||
computed = compute_node_yield(session, node, yield_date, moment)
|
||||
if not computed.eligible:
|
||||
continue
|
||||
try:
|
||||
with session.begin_nested():
|
||||
session.add(_to_row(node, computed, partial))
|
||||
node.recoverable_stake_amount = money(computed.remaining_points - computed.points_delta)
|
||||
except IntegrityError:
|
||||
skipped += 1
|
||||
continue
|
||||
@@ -204,6 +224,15 @@ def _proven_end(interval: UptimeInterval, node: ComputeNode, now: datetime) -> d
|
||||
return now
|
||||
|
||||
|
||||
def _remaining_stake(session: Session, node: ComputeNode) -> Decimal:
|
||||
if node.recoverable_stake_amount is not None:
|
||||
remaining = to_decimal(node.recoverable_stake_amount)
|
||||
if remaining <= 0:
|
||||
return Decimal("0")
|
||||
return money(remaining)
|
||||
return _remaining_points(session, node)
|
||||
|
||||
|
||||
def _remaining_points(session: Session, node: ComputeNode) -> Decimal:
|
||||
paid_rows = session.scalars(select(NodeYield.points_delta).where(NodeYield.node_id == node.id)).all()
|
||||
paid = sum((to_decimal(item) for item in paid_rows), Decimal("0"))
|
||||
|
||||
Reference in New Issue
Block a user