按显卡世代和创世编号结算回本,机房批量接入需审核网段并用硬件指纹防克隆。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-11 00:30:46 +08:00
parent 7d29be8b17
commit 0740016ad8
22 changed files with 1554 additions and 96 deletions
+64
View File
@@ -23,6 +23,8 @@ from sqlalchemy import (
Enum,
ForeignKey,
Index,
Boolean,
Float,
Integer,
Numeric,
String,
@@ -35,6 +37,7 @@ from sqlalchemy.types import JSON, TypeDecorator
from app.clock import utcnow
from app.database import Base, UtcDateTime
from app.money import money
from app.services.hardware import display_node_id, is_genesis
class PreciseDecimal(TypeDecorator):
@@ -109,6 +112,12 @@ class OrderStatus(str, enum.Enum):
REJECTED = "rejected"
class DatacenterStatus(str, enum.Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
class User(Base):
"""用户。一个钱包地址对应一名用户,可绑定多台算力节点。"""
@@ -125,6 +134,7 @@ class User(Base):
yields: Mapped[list[NodeYield]] = relationship(back_populates="user")
withdrawals: Mapped[list[WithdrawalLog]] = relationship(back_populates="user")
orders: Mapped[list[LicenseOrder]] = relationship(back_populates="user")
datacenter_applications: Mapped[list[DatacenterApplication]] = relationship(back_populates="user")
class ComputeNode(Base):
@@ -140,6 +150,7 @@ class ComputeNode(Base):
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True)
sn: Mapped[str] = mapped_column(String(64), unique=True, index=True, comment="节点唯一序列号")
node_index: Mapped[int] = mapped_column(Integer, unique=True, comment="激活顺序,从 1 开始")
purchase_amount: Mapped[Decimal] = mapped_column(MONEY, comment="授权购买金额")
license_months: Mapped[int] = mapped_column(Integer, default=12)
licensed_at: Mapped[datetime] = mapped_column(UtcDateTime, comment="授权开始时间")
@@ -148,16 +159,69 @@ class ComputeNode(Base):
last_heartbeat_at: Mapped[datetime | None] = mapped_column(UtcDateTime)
callback_url: Mapped[str | None] = mapped_column(String(512), comment="可选的任务推送地址")
node_token: Mapped[str | None] = mapped_column(String(128), unique=True, comment="挂机客户端凭证,审核通过后生成")
gpu_model: Mapped[str | None] = mapped_column(String(128), comment="心跳上报的显卡型号")
vram_gb: Mapped[float | None] = mapped_column(Float, comment="显存,GB")
cpu_count: Mapped[int | None] = mapped_column(Integer, comment="CPU 逻辑核心数")
memory_gb: Mapped[float | None] = mapped_column(Float, comment="内存,GB")
hardware_weight: Mapped[float] = mapped_column(Float, default=0.05, comment="硬件算力权重")
gpu_generation: Mapped[str] = mapped_column(String(8), default="10", comment="显卡世代:40、30 或 10")
recovery_days: Mapped[int] = mapped_column(Integer, default=540, comment="质押回本目标天数")
generation_locked: Mapped[bool] = mapped_column(default=False, comment="世代已按上报显卡锁定")
recoverable_stake_amount: Mapped[Decimal | None] = mapped_column(MONEY, comment="尚未返还的质押金")
is_whitelist: Mapped[bool] = mapped_column(Boolean, default=False, comment="白名单节点,跳过单 IP 限制")
client_ip: Mapped[str | None] = mapped_column(String(64), index=True, comment="最近一次注册或心跳的客户端 IP")
hardware_fingerprint: Mapped[str | None] = mapped_column(
String(64), unique=True, comment="显卡 UUID、主板与网卡合成的硬件指纹"
)
created_at: Mapped[datetime] = mapped_column(UtcDateTime, default=utcnow)
updated_at: Mapped[datetime] = mapped_column(UtcDateTime, default=utcnow, onupdate=utcnow)
user: Mapped[User] = relationship(back_populates="nodes")
order: Mapped[LicenseOrder | None] = relationship(back_populates="node")
@property
def display_node_id(self) -> int:
return display_node_id(self.node_index)
@property
def genesis(self) -> bool:
return is_genesis(self.node_index)
yields: Mapped[list[NodeYield]] = relationship(back_populates="node")
tasks: Mapped[list[ComputeTask]] = relationship(back_populates="node")
uptime_intervals: Mapped[list[UptimeInterval]] = relationship(back_populates="node")
class IPWhitelist(Base):
"""管理员维护的豁免地址。可以是单 IP,也可以是机房网段,例如 203.0.113.0/24。"""
__tablename__ = "ip_whitelist"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
ip_address: Mapped[str] = mapped_column(String(64), unique=True, index=True)
note: Mapped[str | None] = mapped_column(String(128))
created_at: Mapped[datetime] = mapped_column(UtcDateTime, default=utcnow)
class DatacenterApplication(Base):
"""机房大客户申请。通过后把出口网段写入 IP 白名单。"""
__tablename__ = "datacenter_applications"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True)
contact: Mapped[str] = mapped_column(String(128))
node_count: Mapped[int] = mapped_column(Integer)
subnet_ip: Mapped[str] = mapped_column(String(64))
hardware_proof: Mapped[str] = mapped_column(Text)
gpu_generation: Mapped[str] = mapped_column(String(8))
status: Mapped[DatacenterStatus] = mapped_column(_enum(DatacenterStatus), default=DatacenterStatus.PENDING, index=True)
note: Mapped[str | None] = mapped_column(String(500))
created_at: Mapped[datetime] = mapped_column(UtcDateTime, default=utcnow)
reviewed_at: Mapped[datetime | None] = mapped_column(UtcDateTime)
user: Mapped[User] = relationship(back_populates="datacenter_applications")
class UptimeInterval(Base):
"""一段连续在线时间。心跳首次上线时打开,超时或授权到期时关闭。"""