bitcore-wallet-service MongoDB 存储层源码解析钱包、协付人与交易数据如何高效索引与查询【免费下载链接】bitcore-wallet-serviceA multisig, HD Bitcoin and Bitcoin Cash wallet service. Used by Copay.项目地址: https://gitcode.com/gh_mirrors/bi/bitcore-wallet-servicebitcore-wallet-service是一款支持多签名、HD 派生的比特币 / 比特币现金钱包服务Copay 等钱包产品就构建在它之上。它最底层的账本由 MongoDB 承担钱包Wallet、协付人Copayer、交易提案TxProposal、地址、通知、会话等核心数据全部落在这里。这篇文章带你走进它的 MongoDB 存储层源码看看 13 个集合是如何设计的、索引为什么建在这些字段上、以及高频查询是如何被优化的——即使你没写过后端也能看懂这套架构的完整思路。一图看懂存储层在整个服务中的位置整个服务的启动链路非常清晰入口bws.js启动 HTTP 服务默认端口 3232lib/server.js中的WalletService.initialize按顺序完成三件事初始化存储 → 初始化消息代理 → 初始化法币汇率服务lib/storage.js导出的Storage类通过connect方法读取配置中的mongodb://.../bws连接串建立数据库连接后立即调用_createIndexes把所有索引建好。也就是说存储层是全局单例其他所有服务邮件服务、区块链监控、推送服务都通过new Storage()拿到同一个实例来读写数据。MongoDB 连接配置在config.js的storageOpts.mongoDb.uri中默认指向本机的mongodb://localhost:27017/bws。13 个集合数据模型的完整地图lib/storage.js顶部用一个collections对象集中定义了全部集合名这是理解数据布局的总目录集合名存什么谁在用它wallets钱包完整信息m/n、派生策略、协付人列表钱包模块copayers_lookup协付人反向索引copayerId ↔ walletId登录 / 查询我的钱包txs交易提案TxProposal多签流程的核心交易模块addresses钱包地址及注册/同步状态地址管理notifications钱包内通知消息广播preferences每个协付人的偏好设置偏好模块email_queue待发送 / 失败重发邮件邮件服务cache万能缓存历史缓存、余额缓存、手续费缓存等各服务fiat_rates法币汇率快照汇率服务tx_notes交易备注备注模块sessions协付人会话登录凭证认证push_notification_subs推送设备 token 订阅推送服务tx_confirmation_subs交易确认时通知我订阅确认订阅 值得注意的设计cache是一个多用途集合靠type字段区分不同缓存historyCacheV8、balanceCache、feeLevels、historyStream等再靠key定位具体条目。这让新增一种缓存无需新建集合非常实用。索引设计每个高频查询都有专属复合索引_createIndexes方法lib/storage.js是全文最值得细读的部分。它的原则很简单索引字段组合 查询条件字段组合。挑几个典型来看txs集合建了 4 个索引(walletId, id)按 ID 查单笔交易(walletId, isPending, txid)一次覆盖查这个钱包所有待签交易 去重两个需求(walletId, createdOn 降序)支撑按时间倒序列出交易历史这种最频繁的列表页查询单独的txid索引则支持全局按链上交易哈希反查fetchTxByHash。copayers_lookup建了copayerId和walletId两个单字段索引这正是下一节要讲的双向查找。addresses建了 4 个索引包括(walletId, createdOn)、(address)、(address, beRegistered)、(walletId, address)分别对应列出钱包地址、全库按地址查、找未注册地址、查某地址是否属于某钱包四种场景。这种先想清楚查询再建索引的做法是多签钱包服务在数据量增长后依然能快速响应的关键。钱包与协付人如何存储与高效查询钱包upsert 一条搞定storeWallet使用 MongoDB 的updateupsert: truew: 1写确认级别钱包存在就整条覆盖更新不存在就插入。配合wallets.id索引保存和查找都是 O(1) 级别的点查。钱包对象本身由lib/model/wallet.js定义包含m/n多签参数、copayers协付人数组、publicKeyRing、派生策略等字段。协付人反向索引一个巧妙的 lookup 集合多签钱包有个高频需求我登录了请告诉我我参与了哪些钱包。如果协付人只嵌在钱包文档里就得把所有钱包扫一遍。BWS 的解法是维护一张独立的copayers_lookup表。storeWalletAndUpdateCopayersLookup每次保存钱包时会把该钱包下每个协付人的{ copayerId, walletId, requestPubKeys }拆成独立文档——先删旧、再插新保证与钱包严格同步。于是按copayerId查 → 我有哪些钱包并直接拿到签名公钥按walletId查 → 某钱包有哪些成员。一个集合、两个方向的索引避免了两张大表的重复存储。交易数据TxProposal多签流程的心脏txs集合存放的每笔文档都是一条交易提案贯穿创建 → 多人签名 → 广播的完整多签流程。存储层提供了几类典型查询模式fetchTx{ id, walletId }双条件查单笔精确命中(walletId, id)复合索引fetchTxByHash只凭链上txid反查命中独立txid索引用于外部系统这笔交易是不是我们钱包发的fetchTxs/fetchBroadcastedTxs按 Unix 时间戳窗口$gte/$lte 钱包过滤 createdOn倒序 limit完美匹配客户端分页拉取交易历史的场景_completeTxData取出原始文档后再回查一次钱包把交易里冰冷的creatorId、copayerId补全成协付人姓名——存储层顺手完成了数据富化前端拿到即可直接展示。cache 集合的进阶玩法用索引做分页缓存cache集合最有意思的地方是它把索引字段当成了分页游标。以getTxHistoryCacheV8为例交易按确认顺序被赋予递增的position存入读取时先取historyCacheStatusV8状态文档拿到tipIndex最新一条的位置再用key: { $gte: firstPosition, $lt: lastPosition }加sort({ key: -1 })直接定位到第 skip1 条到第 skiplimit 条——MongoDB 复合索引(walletId, type, key)让这次范围查询几乎不需要排序天然就是 O(1) 分页。余额缓存balanceCache则把整个地址列表做 RIPEMD160 哈希作为key配合时间戳实现5 分钟有效期内直接返回缓存大幅减轻区块浏览器压力。总结5 个可以照搬的设计要点集合即领域13 个集合与业务实体一一对应边界清晰索引跟着查询走每个高频查询都预先设计复合索引避免运行时全表扫描upsert w:1 统一写入语义幂等、可重放天然适配分布式重试lookup 表做反向索引copayers_lookup用一个集合解决双向关联查询单一 cache 集合 type/key 双字段灵活扩展缓存种类索引字段兼做分页游标。这套存储层代码虽只集中在lib/storage.js约 1400 行和lib/model/下的数据模型中却完整演示了如何用 MongoDB 支撑一个高并发多签钱包服务——值得每一位后端同学精读。【免费下载链接】bitcore-wallet-serviceA multisig, HD Bitcoin and Bitcoin Cash wallet service. Used by Copay.项目地址: https://gitcode.com/gh_mirrors/bi/bitcore-wallet-service创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考