From efa6cffecd4eab378e2272ff932544f174579fff Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Thu, 9 Jul 2026 08:11:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(pay):=20paywatch=20selfcheck=20+=20money-c?= =?UTF-8?q?ritical=20=E9=AA=8C=E8=AF=81=20runbook(#34/34A)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit selfcheck 子命令:用 PAY_ACCOUNT_XPUB 打印前 N 个派生收款地址,供与自己钱包/Ian Coleman 逐个核对(派生错=钱打到无私钥地址)。README 加『收真钱前必过』四步验证:①金标准测试 ②Ian Coleman 独立交叉核对 ③真钱包 A selfcheck 比对 ④真链 1 USDT 收→控→测→归闭环。 Co-Authored-By: Claude Opus 4.8 --- pay/README.md | 34 ++++++++++++++++++++++++++++++++++ pay/cmd/paywatch/main.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/pay/README.md b/pay/README.md index 82cf5c6..50ddb66 100644 --- a/pay/README.md +++ b/pay/README.md @@ -25,6 +25,40 @@ xpub 派生,**不持私钥**),轮询 TronGrid 侦测到账、确认后标记订 助记词、Coin=TRX 对照前 3 个地址。一致 = 派生实现可信;不一致 = 有 bug,别上线。 5. 助记词 A/B 分开冷存 + Bitwarden。**只把 xpub_A 交给本服务。** +## Money-critical 验证(收任何真钱之前必须全过) + +派生错 = 把买家的钱打到你没私钥的地址,钱直接丢。上真钱前逐步核死: + +**① 代码自检(金标准向量)** +```bash +cd pay && go test ./internal/wallet -run TestKnownVector -v # 必须 PASS +``` + +**② 独立交叉核对(证明代码 vs 独立实现一致)** —— 断网开 Ian Coleman `bip39-standalone.html`, +输入测试助记词 `abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about`, +Coin=TRX、BIP44,核对 Derived Addresses 前 3 行必须等于: +``` +m/44'/195'/0'/0/0 TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH +m/44'/195'/0'/0/1 TSeJkUh4Qv67VNFwY8LaAxERygNdy6NQZK +m/44'/195'/0'/0/2 TYJPRrdB5APNeRs4R7fYZSwW3TcrTKw2gx +``` +且 Account Extended Public Key = `xpub6D1AabNHCupeiLM65ZR9UStMhJ1vCpyV4XbZdyhMZBiJXALQtmn9p42VTQckoHVn8WNqS7dqnJokZHAHcHGoaQgmv8D45oNUKx6DZMNZBCd`。 + +**③ 你的真钱包 A(护你自己钱的那道闸,最重要)** —— 用你钱包 A 的真 account xpub: +```bash +PAY_ACCOUNT_XPUB="<你钱包A的account xpub>" go run ./cmd/paywatch selfcheck 5 +``` +把输出的地址,和你钱包 A(在 Ian Coleman 里同一 xpub、或 TronLink/imToken 里钱包 A)显示的 +地址 0..4 **逐个比对**。**全部一致才可上线收款;有一个不一致,立即停,别收任何钱。** + +**④ 真链闭环(Phase E,最终证明)** —— 小额: +1. `POST /order` 拿地址 → 往它转 **1 USDT(TRC20)**。 +2. 用你钱包 A 打开 → **确认这 1 USDT 出现在你钱包 A 名下**(证明你确实控制这个地址)。 +3. `GET /order/{id}` 应变 `paid`(证明 watcher 侦测正确)。 +4. 用 `cmd/sweep` 把这 1 USDT 归集到冷钱包 B → **确认 B 收到**(证明签名/广播正确)。 + +全过 = 收→控→测→归 四环闭合,可放量。 + ## 运行 ```bash diff --git a/pay/cmd/paywatch/main.go b/pay/cmd/paywatch/main.go index bff656b..863f19c 100644 --- a/pay/cmd/paywatch/main.go +++ b/pay/cmd/paywatch/main.go @@ -16,6 +16,7 @@ package main import ( "context" + "fmt" "log/slog" "net/http" "os" @@ -28,6 +29,7 @@ import ( "github.com/wangjia/pangolin/pay/internal/pay" "github.com/wangjia/pangolin/pay/internal/store" "github.com/wangjia/pangolin/pay/internal/tron" + "github.com/wangjia/pangolin/pay/internal/wallet" "github.com/wangjia/pangolin/pay/internal/watcher" ) @@ -38,7 +40,39 @@ func env(k, def string) string { return def } +// selfcheck prints the first N receiving addresses derived from PAY_ACCOUNT_XPUB. +// MONEY-CRITICAL: compare these against your own wallet app / iancoleman.io for +// the SAME account xpub. If they don't match, the service would hand buyers +// addresses you cannot spend — do NOT accept any payment until they match. +func selfcheck() { + xpub := os.Getenv("PAY_ACCOUNT_XPUB") + if xpub == "" { + fmt.Fprintln(os.Stderr, "PAY_ACCOUNT_XPUB is required") + os.Exit(1) + } + n := 5 + if len(os.Args) > 2 { + if v, err := strconv.Atoi(os.Args[2]); err == nil && v > 0 { + n = v + } + } + fmt.Println("Derived receiving addresses (compare against your wallet for this xpub):") + for i := 0; i < n; i++ { + addr, err := wallet.AddressFromAccountXpub(xpub, 0, uint32(i)) + if err != nil { + fmt.Fprintf(os.Stderr, "derive %d: %v\n", i, err) + os.Exit(1) + } + fmt.Printf(" m/44'/195'/0'/0/%d -> %s\n", i, addr) + } +} + func main() { + if len(os.Args) > 1 && os.Args[1] == "selfcheck" { + selfcheck() + return + } + log := slog.New(slog.NewJSONHandler(os.Stdout, nil)) xpub := os.Getenv("PAY_ACCOUNT_XPUB")