Files

165 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HyperNode-AI 说明
HyperNode-AI 是算力节点的控制面。节点程序部署在各处,本服务负责登记节点、接收心跳、下发模拟任务、按天计算积分,并记录提现。
时间全部使用 UTC。每日清算的触发时刻是北京时间 00:05,入账的是上一个已经结束的 UTC 日。
本地开发直接启动时监听 8000。服务器按 [部署.md](部署.md) 安装后监听 33333。
```bash
pip install -r requirements.txt
python -m app
```
## 目录
| 路径 | 内容 |
|---|---|
| `app/models.py` | 数据表 |
| `app/api.py` | HTTP 接口 |
| `app/services/heartbeat.py` | 心跳与离线判断 |
| `app/services/tasks.py` | 任务下发与 nonce 校验 |
| `app/services/yield_engine.py` | 产出公式与按日入账 |
| `app/jobs/daily_yield.py` | 凌晨清算入口 |
| `app/scheduler.py` | 进程内定时任务 |
| `app/settle.py` | 可单独执行的清算命令 |
| `frontend/` | Vue 3 控制台源码。改完后在该目录执行 `npm run build` |
| `web/` | 控制台构建结果,服务启动后打开 `/` |
| `deploy/manage.sh` | Ubuntu 22.04 交互式部署,支持 `curl \| bash` |
## 数据表
业务表:
- `users`:钱包地址、可选用户名和密码、角色 `user` / `admin`、注册时间。
- `compute_nodes`:节点 SN、购买金额、12 个月授权、`online` / `offline`、最后心跳。可选 `callback_url` 用来推送任务。
- `node_yields`:每日算力产出和积分。同一节点同一天只有一条。
- `withdrawal_logs`:提现金额、资产符号、链上哈希、状态。
计算用的表:
- `uptime_intervals`:一段连续在线时间。
- `compute_tasks`:下发的模拟任务和回传结果。
- `scheduler_locks`:多台服务实例同时跑定时任务时互斥。
## 产出公式
节点按激活顺序编号。第 1 台显示为 `1001`,第 2 台为 `1002`,即 `display_node_id = 1000 + 激活序号`。首页和控制台的全网节点数同样加上 1000:3 台在线显示为 1003。机器序列号仍用于心跳和客户端,不对外当作节点编号。
编号 `1001` 到 `1020` 是创世特权节点,回本固定为 30 天,每日基础释放 = `1500 / 30` = 50。上报显卡只会记录型号,不会改掉这 30 天。从 `1021` 起按显卡世代回本。
同一个公网 IP 只能绑定一台普通节点。再挂第二台时会拒绝,并提示去申请机房白名单。白名单可以是单 IP,也可以是网段,例如 `203.0.113.0/24`;请求 IP 落在网段里,或节点本身被标成白名单,就可以挂多台。客户端用心跳上报硬件指纹,由显卡 UUID、主板序列号和网卡地址合成,数据库里唯一。指纹重复会按虚拟机克隆拒绝。机房申请至少 5 台,且证明材料不能包含 10 系、20 系或核显;管理员通过后,该网段自动写入白名单。已经进网段的机器如果上报 10 系及更低显卡,不能混入这批授权。订单审核开通时不占用管理员自己的 IP,节点客户端第一次心跳才绑定地址。
心跳上报显卡后,世代会被锁定,并决定 1500 USDT 质押的回本天数。当日积分 = 购买金额 / 回本天数 × 当日表现系数。表现系数就是平台收益加权系数。积分优先冲减待返还质押,直到质押返还完毕。
- 创世节点 `1001`–`1020`:30 天,每日基础释放 = `1500 / 30`
- 40 系及以上,例如 RTX 4090、4080、4070:210 天,每日基础释放 = `1500 / 210`
- 30 系,例如 RTX 3090、3080、3070、3060 Ti、3060:365 天,每日基础释放 = `1500 / 365`
- 10 系及更低、20 系,以及核显:540 天,每日基础释放 = `1500 / 540`
- 在线率 = 当天授权窗口内的在线秒数 / 当天授权有效秒数
- 任务响应率 = 校验通过且执行成功的任务数 / 当天下发任务数
- 当天没有下发任务时,表现系数 = 在线率
- 有任务时,表现系数 = `0.7 × 在线率 + 0.3 × 响应率`
- 第 21 台及之后,未上报显卡前按 540 天计算。一旦识别到型号就锁定,之后不再改档
硬件权重仍会记录:RTX 3060 及以上为 `1.0`,RTX 20 系和 GTX 1060–1660 为 `0.5`,更旧或更低的独显为 `0.05`,没有独显为 `0.02`。回本速度看世代,不看这个权重。10 系的回本天数长于 12 个月授权时,授权结束时间会延到回本周期结束。
开放的在线区间如果最后心跳已超过 15 分钟,只计算到最后一次心跳。
权重可用环境变量 `ONLINE_WEIGHT`、`TASK_WEIGHT` 修改,默认 `0.7` 和 `0.3`。
## 运行中的三件事
服务进程里有三个定时任务:
1. 每 5 分钟检查心跳。超过 15 分钟没有心跳,或授权到期,节点改为 `offline`,在线区间结束在最后一次心跳。
2. 每 5 分钟向在线、授权仍有效、且没有未完成任务的节点随机下发任务,每轮最多 20 台。任务带 `task_id` 和一次性 `nonce`。节点若配置了 `callback_url`,会额外 POST 一次;否则节点用拉取接口领取。
3. 北京时间 00:05 结算上一个已结束的 UTC 日,写入 `node_yields`。同一天已有记录则跳过。
回传必须带上任务里的 `nonce`。超时、重复回传、nonce 不匹配都不算成功。
## 登录
普通用户可以注册钱包、用户名和密码。之后用钱包地址,或用户名加密码登录。管理员只能用账号密码。
新部署会在安装结束时打印管理员账号 `admin` 和随机密码,并写入 `/opt/hypernode/.env` 的 `ADMIN_PASSWORD`。已经部署过的机器如果 `.env` 里没有这两项,服务第一次启动会在日志里写一次初始密码:
```bash
journalctl -u hypernode.service | grep 初始密码
```
页面路由:
- `/login` 登录和注册
- `/dashboard` 用户提交 1500 USDT 支付凭证、查看待审核订单、复制已开通节点的 Token 并下载客户端
- `/admin` 管理员审核授权订单,通过后自动开通 12 个月节点;同时看总用户、总节点、总产出和提现
节点心跳、拉任务、回传任务不需要登录。查看全网数据、清算、确认提现需要管理员令牌。
## 接口
前缀都是 `/api/v1`。金额在 JSON 里是字符串。浏览器登录后带 `Authorization: Bearer <token>`。
| 方法 | 路径 | 作用 |
|---|---|---|
| POST | `/auth/register` | 注册钱包,可带用户名和密码,返回令牌 |
| POST | `/auth/login` | 钱包地址,或用户名加密码 |
| GET | `/me/dashboard` | 当前用户的节点、订单、积分和提现 |
| POST | `/me/orders` | 提交 1500 USDT 支付凭证 TxID,订单进入待审核 |
| GET | `/me/nodes/{sn}/client` | 下载挂机客户端。节点未开通时返回 403 |
| GET | `/admin/orders` | 管理员:授权订单,待审核排在前面 |
| POST | `/admin/orders/{id}/approve` | 管理员:通过订单,创建节点并签发 Token |
| POST | `/admin/orders/{id}/reject` | 管理员:拒绝订单,不创建节点 |
| POST | `/me/nodes` | 直接登记节点,不签发挂机 Token |
| POST | `/me/withdrawals` | 当前用户申请提现 |
| GET | `/admin/summary` | 管理员:用户数、节点数、总产出 |
| GET | `/admin/users` | 管理员:全网用户 |
| GET | `/admin/nodes` | 管理员:节点状态与今日在线率 |
| GET | `/admin/withdrawals` | 管理员:提现审核列表 |
| POST | `/admin/withdrawals/{id}/confirm` | 管理员:写入链上哈希并完成 |
| POST | `/admin/withdrawals/{id}/reject` | 管理员:拒绝并释放占用的积分 |
| POST | `/users` | 只登记钱包,不设置密码 |
| POST | `/nodes` | 登记节点,必须是该钱包本人或管理员 |
| GET | `/nodes/{sn}` | 查看节点 |
| POST | `/node/heartbeat` | 心跳。已开通节点必须带 `token`,客户端同时上报显卡、显存、CPU 和内存 |
| GET | `/node/{sn}/tasks` | 拉取未完成任务 |
| POST | `/node/task/report` | 回传 `sn`、`task_id`、`success`、`nonce` |
| GET | `/nodes/{sn}/contribution?yield_date=YYYY-MM-DD` | 预览某日产出,不入账 |
| POST | `/yield/settle?yield_date=YYYY-MM-DD` | 手动清算,不传日期则清算昨天 |
| GET | `/nodes/{sn}/yields` | 节点流水 |
| GET | `/users/{wallet}/balance` | 可用积分 |
| POST | `/withdrawals` | 申请提现 |
| POST | `/withdrawals/{id}/confirm` | 写入 `tx_hash` 并完成 |
| POST | `/withdrawals/{id}/reject` | 拒绝并释放占用的积分 |
可用积分 = 已入账积分 − 待处理提现 − 已完成提现。
健康检查是 `/health`,不在 `/api/v1` 下面。
## 配置
生产环境的配置在 `/opt/hypernode/.env`。也可以在启动前用同名环境变量覆盖。
| 变量 | 默认值 | 含义 |
|---|---|---|
| `DATABASE_URL` | 部署后指向 `/var/lib/hypernode/hypernode.db` | 数据库。可改为 PostgreSQL 连接串 |
| `HEARTBEAT_TIMEOUT_MINUTES` | `15` | 心跳超时 |
| `OFFLINE_CHECK_MINUTES` | `5` | 离线巡检间隔 |
| `TASK_DISPATCH_MINUTES` | `5` | 任务下发间隔 |
| `DISPATCH_BATCH_SIZE` | `20` | 每轮最多下发数量 |
| `TASK_DEADLINE_MINUTES` | `10` | 任务回传时限 |
| `ONLINE_WEIGHT` / `TASK_WEIGHT` | `0.7` / `0.3` | 有任务时的表现系数权重 |
| `YIELD_TIMEZONE` | `Asia/Shanghai` | 清算触发时区 |
| `YIELD_HOUR` / `YIELD_MINUTE` | `0` / `5` | 清算时刻 |
| `LICENSE_MONTHS` | `12` | 新节点授权月数 |
| `LICENSE_PRICE` | `1500` | 购买节点授权的金额 |
| `LICENSE_ASSET` | `USDT` | 购买节点授权的资产符号 |
| `DISABLE_SCHEDULER` | `false` | 设为 `true` 时不启动定时任务 |
| `JWT_SECRET` | 空则自动生成并写入数据库 | 登录令牌签名 |
| `JWT_EXPIRE_MINUTES` | `1440` | 令牌有效分钟数 |
| `ADMIN_USERNAME` | `admin` | 初始管理员用户名 |
| `ADMIN_PASSWORD` | 空则首次启动随机生成并写入日志 | 初始管理员密码 |
| `ADMIN_WALLET` | `admin-root` | 初始管理员钱包地址 |
修改 `.env` 后执行 `systemctl restart hypernode`。