Files
pangolin/docs/pay-address-pool-plan.html
T
wangjia 5055afdbd7 docs(pay): 地址池收款模型 plan(折中方案,#34/34A)
从每单唯一 HD 地址改为地址池复用(池=峰值并行度、复用摊薄激活/归集)。规则:最小编号
idle 绑单/无 idle 派生/15min 超时释放/同用户单订单。核心安全点:地址复用迟到付款错配
→ 匹配按 tx+金额+时间戳(晚于建单)、每单唯一金额、孤儿付款记录。双产物 md+html,登记 index。
待用户确认后执行(tier-1)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 14:44:22 +08:00

111 lines
6.9 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pangolin-pay 地址池收款模型 实现计划</title>
<style>
:root{
--bg:#0f1117; --panel:#171a22; --panel2:#1d2129; --fg:#e6e8ee; --fg2:#a8afbd;
--accent:#e0884f; --accent2:#5fb0c9; --ok:#5ec27a; --bad:#e06a6a; --warn:#e0b84f;
--border:#272c36; --mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
--sans:-apple-system,"PingFang SC","Helvetica Neue",Arial,sans-serif;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.7;font-size:15px}
.wrap{max-width:960px;margin:0 auto;padding:48px 24px 96px}
h1{font-size:28px;line-height:1.3;margin:0 0 8px}
.sub{color:var(--fg2);font-size:15px;margin:0 0 32px}
h2{font-size:20px;margin:40px 0 12px;padding-bottom:8px;border-bottom:1px solid var(--border);color:var(--accent)}
p{margin:10px 0}
code{font-family:var(--mono);font-size:.85em;background:var(--panel2);padding:1px 6px;border-radius:5px;color:#f0d9c4}
pre{background:#0a0c11;border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;font-family:var(--mono);font-size:12.5px;line-height:1.5;color:#cdd3df}
.tag{display:inline-block;font-size:12px;font-weight:600;padding:2px 9px;border-radius:999px}
.tag.info{background:rgba(95,176,201,.16);color:var(--accent2)}
ul.ck{list-style:none;padding-left:4px}
ul.ck li{position:relative;padding-left:26px;margin:7px 0}
ul.ck li::before{content:"☐";position:absolute;left:0;color:var(--fg2)}
ul{padding-left:22px} li{margin:6px 0}
.lead{background:linear-gradient(180deg,rgba(224,136,79,.10),transparent);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:0 0 8px}
.small{color:var(--fg2);font-size:13px}
a{color:var(--accent2)} .back{display:inline-block;margin-bottom:24px;font-size:13px}
b{color:#fff}
.warnbox{background:rgba(224,106,106,.08);border:1px solid rgba(224,106,106,.35);border-radius:12px;padding:14px 18px;margin:14px 0}
.warnbox b{color:var(--bad)}
table{width:100%;border-collapse:collapse;margin:16px 0;font-size:13.5px}
th,td{text-align:left;padding:9px 12px;border-bottom:1px solid var(--border);vertical-align:top}
th{color:var(--fg2);font-weight:600;font-size:13px}
</style>
</head>
<body>
<div class="wrap">
<a class="back" href="index.html">← 文档索引</a>
<h1>pangolin-pay 地址池收款模型</h1>
<p class="sub">2026-07-09 · <span class="tag info">阅读版</span> · 执行真相源 <code>docs/superpowers/plans/2026-07-09-pay-address-pool.md</code> · #34/34A 收款模型修订(折中方案)</p>
<div class="lead">
从"每单唯一 HD 地址"改为<b>地址池复用</b>:池大小 = <b>峰值并行度</b>(初始 10,不够动态派生),地址复用摊薄激活/归集成本;仍多地址,<b>避免并发订单金额撞、对账精确</b>。归集成本从 O(订单数) 降到 <b>O(池大小)</b>,激活一次/地址即长期复用。是"单地址(省钱撞单)"与"每单唯一(精确但贵)"的折中。
</div>
<div class="warnbox">
<b>核心风险 · 地址复用的迟到付款错配:</b>地址回收后绑给新订单,若前一个用户超时后才付款,钱打到已绑新订单的地址上 → 可能误配。<b>解法(必须):</b>① 匹配按"到账 tx + 金额 + 时间戳(晚于建单)",<b>不按余额、不只按地址</b>;② <b>每单唯一金额</b>(基准+唯一尾数);③ 匹配不到活跃订单的到账 → 记 <code>orphan_payments</code> 人工对账;④(可选)超时后地址冷却再重绑。
</div>
<h2>设计(规则 + 安全点)</h2>
<ul>
<li><b>地址池</b>:HD 派生地址(<code>m/44'/195'/0'/0/i</code>),状态 <code>idle|busy</code>,初始 10。</li>
<li><b>建单</b>:绑<b>最小编号 idle</b> 地址→busy;无 idle → 派生新地址入池。<b>原子</b>(事务/行锁,防并发抢同址)。</li>
<li><b>每单唯一金额</b> ⭐:基准 + 唯一尾数,当前活跃订单间不撞;复用地址上旧单迟到付款不误配新单。</li>
<li><b>15min 超时</b> → expired → 地址回 idle(可选冷却)→ 提示重建订单。</li>
<li><b>付款完成</b> → paid → 地址回 idle(注意:地址里仍有 USDT 待归集,idle 只表示可重绑)。</li>
<li><b>匹配</b>:找"到该地址 + 金额命中 + 区块时间晚于建单"的转入 tx。</li>
<li><b>同用户单订单</b>:同时只能一个活跃订单。</li>
<li><b>归集</b>:定期扫池内有余额地址→冷钱包(能量租赁 ~$0.1–1/笔);池 ~10,笔数有界;与 busy 解耦。</li>
</ul>
<h2>Phase A — 数据模型</h2>
<ul class="ck">
<li><b>A.1</b> <code>addresses(id, addr_index唯一, address, state, activated, created_at)</code>,按需派生入池。</li>
<li><b>A.2</b> <code>pay_orders</code><code>user_ref / address_id / expect_amount(唯一金额) / matched_tx_id</code></li>
<li><b>A.3</b> <code>orphan_payments(tx_id唯一, address, value, block_ts, handled)</code></li>
<li><b>A.4</b> 复用 addr_cursor 派生新址;迁移 + 索引(status/address/user_ref)。</li>
</ul>
<h2>Phase B — 建单</h2>
<ul class="ck">
<li><b>B.1</b> CreateOrder(user, sku):校验同用户无活跃单 → 原子选最小 idle 置 busy(无则派生)→ 分配唯一金额 → 写 pending(TTL 15min)。</li>
<li><b>B.2</b> 唯一金额分配(基准+唯一尾数,当前活跃单间不撞)。</li>
<li><b>B.3</b> GetOrder + releaseAddress 公共方法(paid/expired 复用)。</li>
</ul>
<h2>Phase C — watcher(匹配/释放/孤儿)</h2>
<ul class="ck">
<li><b>C.1</b> 匹配:pending 单查其地址转入,value 命中唯一金额 且 <code>block_ts&gt;created</code> → paid + 记 tx + 释放地址。</li>
<li><b>C.2</b> 超时:pending 过期 → expired + 释放地址(可选冷却)。</li>
<li><b>C.3</b> 孤儿:匹配不到活跃订单的到账 → 记 orphan_payments(去重)。</li>
<li><b>C.4</b> 幂等(tx_id 去重)+ 崩溃恢复;TronGrid 取 block_timestamp 供时间过滤。</li>
</ul>
<h2>Phase D — 归集</h2>
<ul class="ck">
<li><b>D.1</b> 扫池内有余额地址 → 冷钱包(能量租赁,先手动 runbook);与订单状态解耦。</li>
</ul>
<h2>Phase E — 测试 + 真链</h2>
<ul class="ck">
<li><b>E.1</b> 单测:并发不抢同址、复用后迟到付款不误配、唯一金额、超时释放、孤儿、幂等、崩溃恢复、同用户单订单。</li>
<li><b>E.2</b> 真链:两并发订单各绑地址付款;一单超时释放后新单复用同址,旧地址补"旧金额"付款应成孤儿不误配;归集。</li>
</ul>
<h2>不在本轮 / 取舍</h2>
<ul>
<li>GasFree(#35,免 TRX 但每笔固定 1 USDT,更适合少地址批量,另议)。</li>
<li>能量租赁自动化(先手动);池增长上限(先宽松);发货侧(独角数卡 + /internal/codes/mint)另排。</li>
</ul>
<p class="small" style="margin-top:32px">相关:<a href="crypto-tx-engine-plan.html">加密货币交易引擎(已实现钱包/tron/sweep)</a> · <a href="payment-clean-usdt-plan.html">#34 收款闭环全景</a></p>
</div>
</body>
</html>