Skip to content

refactor: 落地 P0 —— 移除 TRON 源码密钥 + 全局运行态收口成可注入 Registry - #2

Open
lbtsm wants to merge 3 commits into
mainfrom
refactor/protocol-registry
Open

refactor: 落地 P0 —— 移除 TRON 源码密钥 + 全局运行态收口成可注入 Registry#2
lbtsm wants to merge 3 commits into
mainfrom
refactor/protocol-registry

Conversation

@lbtsm

@lbtsm lbtsm commented Aug 18, 2026

Copy link
Copy Markdown
Owner

落地 WORK-19 评审里的两条 P0,从 main 新切分支。范围只到「拿掉源码密钥」+「全局运行态收口成可注入对象」,Router 命令管道(P0-3)和链工厂/连接拆分(P1)没有动。

1. TRON API Key 从源码搬到配置

chains/tron/conn.goConnect 里写死了一份 TronGrid API Key,随二进制发布、全环境共用、轮换必须改代码。改成从链配置的 apiKey opt 读取。

与评审建议的一点差异:没有做成缺失即启动失败。自建 FullNode 的 gRPC 端点本来不需要 key,硬性要求会让自建部署起不来;所以空值只告警。已在 config.json.example 补了 tron 示例段。

已泄露的那把 key 需要运维确认并轮换,代码这边只是不再兜底。

2. 跨链运行态收口成 mapprotocol.Registry

原先 MAP client、各链合约 caller、height provider、light manager 全是 internal/mapprotocol 的包级变量,5 处链构造 + expose proof API 都在写。Gin 每个 proof 请求一个 goroutine,而 chains.Proffer 每次返回的是同一个原型对象,所以那些裸 map 是并发写的 —— 离 concurrent map write 崩进程只差流量。

  • 新增 Registry:一个所有者、访问全部加锁、需要遍历的地方返回快照。
  • 查不到的条目现在报 ErrNotRegistered,而不是 panic(Map2OtherHeight[id]() 这种取值即调用)或静默返回 nil height(Get2MapHeight / GetNodeTypeByManager 这些函数变量在 Init 之前默认返回 (nil, nil),调用方直接对 nil 做 Cmp)。
  • 4 个 Init*(各装一个闭包进函数变量)合并成一个 LightManager 对象,依赖收窄到 ContractCaller 这一个方法的接口,因此可以脱离 RPC 单测。
  • Registry 仍通过 Default() 取用,进程内实例没变;变的是它现在是个能被传进去的值 —— 下一步(把 CommonSync/writer/proof service 改成构造函数注入)才有落点。

3. 顺手消掉 5 份重复的 Proffer.Connect

bsc / matic / eth2 / ethereum / tron 各有一份几乎一样的 Connect。每份都把注册逻辑包在 sync.OnceFunc 里,但那个 Once 是在函数内部现场 new 的,所以每次调用都会重新执行 —— 也就是每个 proof 请求都重跑一遍共享状态注册,并且新建一条连接从不关闭。

抽成 chain.ProofConnector:按 (chainId, endpoint) 缓存连接、只注册一次、ABI 解析错误不再被 _ 吞掉。5 份实现各剩一行。

影响面

  • 配置格式:兼容。新增可选 opt apiKey(仅 tron)。
  • 链上交互 / ABI / 交易内容:无变化。
  • 行为收紧(有意):未注册的链从 panic / nil 变成显式 error;proof 请求复用连接而不是每次新建。
  • MosMapping(现 MosAddress)与 GetDataByManager(现 LightManagerData)本来就只写不读,这次保留但没有新增调用方,可以后续清理。

自测

go1.26.3 darwin/arm64

$ make build
  >  Building compass...
cd cmd/compass && go build -ldflags "..." -o ../../build/compass
(通过)

$ gofmt -l chains internal cmd core pkg config
(无输出)

$ go test ./internal/chain/ ./internal/mapprotocol/ ./internal/expose/... ./internal/observability/ ./pkg/blockstore/ -race -count=1
ok  	github.com/mapprotocol/compass/internal/chain	2.249s
ok  	github.com/mapprotocol/compass/internal/mapprotocol	2.483s
?   	github.com/mapprotocol/compass/internal/expose	[no test files]
?   	github.com/mapprotocol/compass/internal/expose/handler	[no test files]
ok  	github.com/mapprotocol/compass/internal/expose/service	2.615s
ok  	github.com/mapprotocol/compass/internal/observability	2.707s
ok  	github.com/mapprotocol/compass/pkg/blockstore	3.051s

新增 11 个测试,其中两个专门跑 -race 复现原来的并发写场景:TestRegistryConcurrentAccess(16 组读写 goroutine 打同一份状态)、TestProofConnectorConcurrentConnect(32 个 goroutine 同时 Connect,断言只 dial 一次)。

go vet ./... 只剩 issue 里已声明的三项既有问题(corechains/sol 测试包编译不过、internal/stream/btc.go 重复 json tag,WORK-12 在跟)。config.Test_GetPrivateKeyFromFile 失败与本分支无关:它写死了别人机器上的 keystore 路径 /Users/xm/Desktop/...git diff origin/main -- config/ 对该文件无改动。

未跑需要真实链 RPC / keystore 的集成测试,未发起任何链上交易。

🤖 Generated with Claude Code

lbtsm and others added 3 commits August 18, 2026 17:14
The TronGrid API key was hardcoded in Connect, so it shipped in every
binary and could not be rotated without a release, and every environment
shared one credential. Take it from the chain's `apiKey` opt instead.

It stays optional: a self hosted FullNode gRPC endpoint needs no key, so
an empty value only warns rather than failing startup. Deployments that
talk to a hosted gateway must now set `apiKey` in config — and the leaked
key needs rotating regardless.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The MAP client, per-chain contract callers, height providers and the light
manager lived in package level vars in internal/mapprotocol, written by
five chain constructors and by the expose proof API. Gin serves each proof
request on its own goroutine, and chains.Proffer hands every request the
same prototype, so those plain maps were written concurrently — a
concurrent map write away from taking the process down — and tests could
only work by mutating and restoring globals.

Introduce mapprotocol.Registry: one owner for that state, every accessor
guarded, snapshots handed to callers that iterate. Missing entries now
report ErrNotRegistered instead of panicking (Map2OtherHeight was indexed
and called in one expression) or silently returning a nil height (the
Get2MapHeight/GetNodeTypeByManager function vars defaulted to (nil, nil)
until startup swapped them out). The four Init* calls that installed those
closures collapse into one LightManager object built from a client and an
address, which a small ContractCaller interface makes unit testable.

Also fold the five near-identical Proffer.Connect bodies into
chain.ProofConnector. Each wrapped its registration in a sync.OnceFunc
allocated inside the call, so the Once was fresh every time and deduped
nothing: every proof request re-registered shared state and leaked a new
connection. The connector caches per chain and endpoint, registers once,
and stops swallowing ABI parse errors.

Registry is still reached through Default() at the call sites, so the
process wide instance is unchanged; what changed is that it is now a value
that can be passed in, which is what the next step needs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant