- 区块链
- DeFi
- 金融科技
【免费下载链接】bisq
A decentralized bitcoin exchange network
导读
Bisq 1.10.3 是一个以安全加固为核心的发布版本,重点围绕过滤器(Filter)信任边界、交易状态机防护、存款/延迟赔付交易绑定以及支持与争议消息认证四条主线收紧安全策略。本文以 release-notes/1.10.3/notable-changes.md 为主体,结合仓库源码逐项还原每项变更的实现细节与影响面。读完本文,你将掌握:未签名过滤器字段为何被禁用、Core API 如何阻止失败交易被推进、存款与延迟赔付交易的绑定校验如何防止伪造交易注入,以及支持/争议消息如何校验发送者身份,并了解对应的回归测试覆盖。
一、版本概览与兼容性说明
1.10.3 全量提交范围从358b546526到292f438112,共 10 个提交(7 个普通提交、3 个合并提交,涉及 27 个文件)。官方完整记录见 release-notes/1.10.3/full-release-notes.md。
对用户与运营方(operator)而言,本版本的兼容性要点集中在以下几条:
- 应用与打包版本号提升为
1.10.3,最终 tagv1.10.3内含后续的安全加固提交(见 release-notes/1.10.3/notable-changes.md 的 Release Version 一节)。 - 过滤器提供的 BTC 节点与种子节点字段被禁用(后续章节详述)。
- Core API 拒绝为已失败交易确认"已开始支付"。
- 存款交易与延迟赔付交易在处理前执行更严格的绑定校验。
- 交易聊天、ACK、争议开启、对端开启争议与争议结果消息,在发送者签名公钥不匹配期望的交易对手或争议代理时被直接忽略。
由于这是一次安全加固发布,官方建议依赖过滤器、API 交易控制或争议消息处理路径的节点运营方及时升级。
二、未签名过滤器字段禁用:filterProvidedBtcNodes与filterProvidedSeedNodes
2.1 背景:过滤器签名覆盖范围
Bisq 的网络过滤器(Filter)由受信任开发者私钥签名后经 P2P 网络分发,客户端通过校验签名来决定是否应用。问题在于:Filter数据模型中addedBtcNodes与addedSeedNodes两个字段并未被当前过滤器签名所覆盖(参见 core/src/main/java/bisq/core/filter/Filter.java 中addedBtcNodes、addedSeedNodes字段定义及其序列化/反序列化逻辑)。这意味着攻击者一旦能伪造一份不带这些字段签名的过滤器载荷,理论上可诱导客户端把任意的 BTC 节点或种子节点当作"受信任"节点接入,从而发起中间人(MITM)等攻击。
2.2 实现变更
1.10.3 的处理策略是双管齐下:
- 不再持久化/应用:
FilterManager收到来自网络的新过滤器时,不再将getAddedBtcNodes()/getAddedSeedNodes()写回配置文件(saveBannedNodes调用链见 core/src/main/java/bisq/core/filter/FilterManager.java)。同时节点选择逻辑中,这部分来源被置空。 - 启动时忽略历史残留:此前版本可能已把这两个值持久化到配置文件中,1.10.3 的
Config解析层直接忽略filterProvidedBtcNodes/filterProvidedSeedNodes两个命令行选项的取值(选项定义见 common/src/main/java/bisq/common/config/Config.java),防止陈旧残留值在启动时被重新应用。
2.3 源码验证
在 core/src/main/java/bisq/core/btc/nodes/BtcNodesSetupPreferences.java 的selectPreferredNodes方法中,PROVIDED模式下构造候选 BTC 节点流时,filterProvidedBtcNodes来源仅在--ignoreNetworkFilter未开启时才被读取:
Stream<String> filterProvidedBtcNodes = config.ignoreNetworkFilter ? Stream.empty() : config.filterProvidedBtcNodes.stream();最终由FederatedBtcNodeProvider.getNodes(...)将硬编码节点与过滤来源合并(见 core/src/main/java/bisq/core/btc/nodes/FederatedBtcNodeProvider.java)。种子节点侧同理,core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java 只在非ignoreNetworkFilter时才把config.filterProvidedSeedNodes加入种子节点缓存。
回归测试 core/src/test/java/bisq/core/filter/FilterManagerInitializationTests.java 验证了启动时配置文件中残留的filterProvidedBtcNodes、filterProvidedSeedNodes会被清理,而无关的otherOption=keep得以保留。
2.4 对运营方的影响
- 若你的配置文件或启动参数中显式设置了
--filterProvidedBtcNodes/--filterProvidedSeedNodes,1.10.3 将忽略这些值——这是预期行为,并非配置错误。 - 官方说明该处理是临时性的:在新的过滤器实现(覆盖这些字段签名)可用之前,一律按不安全处理,相关值作为空值写入/应用。
三、BTC 费用接收地址过滤器条目临时禁用
3.1 变更内容
过滤器字段btcFeeReceiverAddresses提供的地址被临时从 mempool 服务的 BTC 费用接收地址列表中排除。该路径当前并未被积极使用,预计将由新的过滤器实现接管。DAO 捐赠地址与 Burning Man(焚烧人)接收地址不受影响,继续留在列表中。
3.2 源码现状
Mempool 服务在构造全部 BTC 费用接收者列表时(见 core/src/main/java/bisq/core/provider/mempool/MempoolService.java 的getAllBtcFeeReceivers()),列表来源包括:
daoFacade.getAllDonationAddresses()—— DAO 补偿请求等捐赠地址;- Burning Man 地址列表的 legacy 地址;
- 过滤器提供的
btcFeeReceiverAddresses(BtcFeeReceiverService.getConfiguredReceiverAddresses(...),见 core/src/main/java/bisq/core/dao/burningman/BtcFeeReceiverService.java); - 历史上曾销毁 BSQ 的 Burning Man 候选人的所有地址。
也就是说,过滤器条目在代码路径上依然被读取解析(parseBtcFeeReceiverAddresses对非法配置会回退到仅 Burning Man 配置FeeReceiverConfig.forBurningManOnly()),但 1.10.3 官方说明强调其"临时排除",意图是等待新过滤器实现重新接管该路径。阅读此部分源码时,应以本版本发布说明为准理解该字段的实际生效状态。
3.3 相关测试
- core/src/test/java/bisq/core/provider/mempool/MempoolServiceTest.java 覆盖费用接收地址列表构造行为;
- core/src/test/java/bisq/core/dao/burningman/BtcFeeReceiverServiceTest.java 覆盖接收地址解析与合法校验。
四、失败交易 API 防护(Failed Trade API Guard)
4.1 问题场景
Core API 允许客户端(如bisq-cli)通过confirmPaymentStarted方法确认买家已开始支付,从而驱动交易沿买家协议路径前进。若交易已失败(failed),仍允许推进该状态机不仅违反协议语义,还可能导致资金路径异常。
4.2 实现
在 core/src/main/java/bisq/core/api/CoreTradesService.java 的confirmPaymentStarted(String tradeId, ...)方法中,首先检查是否按买家协议跟进(isFollowingBuyerProtocol),随后:
if (trade.hasFailed()) { throw new FailedPreconditionException("Cannot confirm payment started for a failed trade."); }同时该方法还保留了两道前置约束:
- 交易存款交易必须已确认(
trade.isDepositConfirmed()),否则报错提示"需等 deposit tx 确认"; - 合约与卖方支付账户载荷(
sellerPaymentAccountPayload)必须已收到,否则报错提示"未收到卖方支付账户详情"——桌面客户端禁用确认按钮采用同一条件。
这样,即使 API 客户端误调或恶意调用,失败交易也无法再沿买家协议路径前进。
五、存款与延迟赔付交易绑定加固
这一节对应提交7e7e4ed905(Harden deposit transaction and delayed payout transaction binding),是 1.10.3 最核心的协议层加固之一。
5.1 买家侧校验:对方存款交易必须与本地一致
买家处理DepositTxAndDelayedPayoutTxMessage时(见 core/src/main/java/bisq/core/trade/protocol/bisq_v1/tasks/buyer/BuyerProcessDepositTxAndDelayedPayoutTxMessage.java),执行以下绑定检查后再触碰钱包或交易状态:
- 本地存款交易获取:作为 taker 时直接使用已设置的
processModel.getDepositTx();作为BuyerAsMakerTrade时,因为 maker 尚未设置存款交易,使用processModel.getPreparedDepositTx()(预构建的存款交易)。 - 延迟赔付交易字节一致性:要求对方发来的 DPT 序列化字节与本地
trade.getDelayedPayoutTxBytes()完全一致,否则checkArgument直接失败。 - 规范字段校验:对对方存款交易调用
checkCanonicalDepositTxFields(见 core/src/main/java/bisq/core/trade/validation/DepositTxValidation.java)。 - DPT 输入绑定:调用
DelayedPayoutTxValidation.checkDelayedPayoutTxInput(...)验证 DPT 的输入确实指向该存款交易。 - 本地比对:通过
checkDepositTxMatchesIgnoringWitnessesAndScriptSigs忽略 witness 与 scriptSig 后比对对方存款交易与本地存款交易,防止收到一份序列化合法但绑定到不同交易的存款。
5.2 DPT 输入必须花费存款交易 output 0
core/src/main/java/bisq/core/trade/validation/DelayedPayoutTxValidation.java 的checkDelayedPayoutTxInput严格校验:
- DPT 输入数量必须为 1;
- 该输入的
outpoint.getHash()必须等于存款交易的txId,且outpoint.getIndex() == 0; - 校验失败时抛出:"Input of delayed payout transaction does not point to output 0 of deposit tx"。
这防止了攻击者提交一份"序列化正确但绑定到另一笔存款交易"的 DPT。同一文件中的checkLegacyDelayedPayoutTxOutput还校验 DPT 输出数量为 1、金额等于买家保证金 + 卖家保证金 + 交易金额之和。
5.3 过期存款确认回调不再推进交易
1.10.3 将存款确认回调绑定到其注册时的交易 ID:如果一笔过期的回调(stale callback)在交易当前存款交易已变化后触发,该回调会被忽略,而不是把交易误标记为"存款已确认"。这一改动堵住了状态机在交易迁移期间被旧回调提前推进的窗口。
5.4 存款交易监听器:要求独立的 maker-fee 与 taker-fee 输入
旧实现只统计"输入父交易 ID 命中两个费用交易 ID 的数量"(count == 2),存在一个攻击面:恶意 seller-as-maker 可以用自己 maker 费用交易的不同输出(费用输出、reserved-for-trade 输出、找零输出)构造两笔输入,从而在计数检查中"伪造"一笔从未被 taker 共同注资的假存款交易。
加固后的isConfTxDepositTx(见 core/src/main/java/bisq/core/trade/protocol/bisq_v1/tasks/buyer/BuyerSetupDepositTxListener.java)改为:
- 遍历输入,分别标记
hasMakerFeeInput与hasTakerFeeInput; - 当
takerFeeTxId已知(taker 角色)时,要求两个标志同时为 true——必须各有一个独立的 maker-fee 输入和一个独立的 taker-fee 输入; - 当
takerFeeTxId为 null(buyer-as-maker 角色)时,仅要求 maker-fee 输入存在; - 同时校验存款地址
depositTxAddress作为该交易的输出出现(而非仅作为输入),避免历史交易因多签地址复用被误判为当前存款交易。
六、支持与争议消息认证加固
对应提交292f438112(Harden support and dispute message sender authentication)。
6.1 基础机制:发送者签名公钥比对
SupportManager(core/src/main/java/bisq/core/support/SupportManager.java)是所有支持/争议消息处理的基类。核心校验方法isSenderSignaturePubKeyExpected(...)将解密后的发送者签名公钥与期望的PubKeyRing签名公钥逐字节比对,不匹配则记Ignoring ... because sender signature pubKey does not match the expected peer并返回 false。PubKeyRing为 null 时(本地交易记录缺失期望密钥)同样拒绝处理。
6.2 交易聊天与 ACK 消息
- 交易聊天消息(
onChatMessage)必须来自期望的交易对手:发送者签名公钥需匹配本地交易记录中对应角色的PubKeyRing。 - ACK 消息(
onAckMessage)同样比对发送者;若 ACK 所引用的源消息在本地找不到,则直接忽略,而不是从未经信任的消息数据中解析对端密钥,避免"凭不可信数据推断信任"。
6.3 争议开启:必须匹配合约声明的交易者
DisputeManager(core/src/main/java/bisq/core/support/dispute/DisputeManager.java)中的isDisputeOpenerSignaturePubKeyValid校验:
- 从争议中取出合约(
Contract),缺失则拒绝; - 依据
dispute.isDisputeOpenerIsBuyer()取合约中的买家或卖家PubKeyRing作为期望开启者; - 发送者签名公钥必须匹配该期望开启者——即争议开启者必须与合约中声明的交易者一致,拒绝"冒名开启争议"。
6.4 对端开启的争议与争议结果:必须匹配本地期望的代理
isDisputeAgentSignaturePubKeyValid校验:
- 本地必须存在对应交易的交易记录,否则拒绝(无法认证争议代理);
getExpectedAgentPubKeyRing(trade)得到的期望代理密钥必须与发送者签名公钥匹配;- 载荷中的代理密钥(
dispute.getAgentPubKeyRing())必须等于交易记录的期望代理,防止攻击者在消息载荷里塞入自己声明的代理密钥。
这一校验同时适用于本地期望的调解代理(mediation)、退款代理(refund)与仲裁代理(arbitration);仲裁侧见 core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java,调解侧见 core/src/main/java/bisq/core/support/dispute/mediation/MediationManager.java。
6.5 关键原则:认证失败先于状态变更
所有无效或未认证的消息都在争议列表、聊天状态或持久化被修改之前被忽略。换言之,非法消息不会污染争议列表、不会进入聊天历史、也不会触发磁盘写入——这与"先认证、后变更"的安全顺序一致。
七、回归测试覆盖
1.10.3 为上述加固补齐了聚焦的回归测试,对应源码位置的测试文件包括:
| 测试主题 | 对应源码 | 相关测试 |
|---|---|---|
| 存款/DPT 绑定、DPT 输入校验 | BuyerProcessDepositTxAndDelayedPayoutTxMessage、DelayedPayoutTxValidation | BuyerProcessDepositTxAndDelayedPayoutTxMessageTest.java、DelayedPayoutTxValidationTest.java |
| buyer-as-maker 存款处理 | BuyerSetupDepositTxListener | 覆盖于上述 buyer 测试族 |
| 过滤器/配置路径清理 | FilterManager、Config | FilterManagerInitializationTests.java |
| 费用接收地址列表 | MempoolService、BtcFeeReceiverService | MempoolServiceTest.java、BtcFeeReceiverServiceTest.java |
官方说明还提到新增了对支持 ACK 认证、争议开启者认证、争议代理认证、争议认证期间的失败交易查找、调解争议结果发送者认证的回归覆盖,以及过期存款确认回调与不匹配存款交易被拒绝的用例。
八、发布版本与升级建议
8.1 版本与提交
应用与打包版本号提升为1.10.3(提交7a441aa6fc,Bump version number forv1.10.3),最终 tagv1.10.3包含版本提升提交及后续的存款/DPT 绑定加固与争议消息认证加固提交。
8.2 发布门禁(Release Gate)
作为安全加固发布,官方建议在发布验证中包含以下聚焦测试路径:
- 过滤器/配置路径:确认
filterProvidedBtcNodes、filterProvidedSeedNodes不再被持久化或应用,历史配置残留被忽略/清理; - Core API 失败交易防护:确认
confirmPaymentStarted对hasFailed()交易抛出FailedPreconditionException; - 存款/DPT 绑定:确认对方存款交易、DPT 输入(output 0)、费用输入(独立 maker/taker fee 输入)、过期回调等路径的校验生效;
- 支持/争议消息认证:确认交易聊天、ACK、争议开启、对端开启争议、争议结果消息在发送者签名公钥不匹配时被忽略且不触发状态变更。
8.3 对运营方与开发者的实践建议
- 节点运营方:升级后检查配置文件,若存在
filterProvidedBtcNodes/filterProvidedSeedNodes项,可手动清理或交给 1.10.3 启动逻辑自动处理;依赖过滤器推送 BTC/种子节点的部署需等待新过滤器实现。 - API 集成方:调用
confirmPaymentStarted前需自行检查交易状态(isFailed),避免在失败交易上收到FailedPreconditionException并导致集成流程中断。 - 阅读源码者:如需深入理解过滤器签名与字段覆盖问题,可结合 docs/filter-and-deny-list.md(过滤器与拒绝列表机制文档)与 core/src/main/java/bisq/core/filter/Filter.java(过滤器数据模型)交叉阅读;Burning Man 接收地址机制可参考 docs/burningman/filter-controlled-btc-fee-receiver-routing.md。
结语
1.10.3 没有引入新功能,却是一版对信任边界要求极高的安全修复:它把"未经签名覆盖的过滤器字段"挡在信任圈之外,用FailedPreconditionException阻断失败交易的异常推进,以"字节级一致 + 输入指向 output 0 + 独立费用输入 + 回调绑定交易 ID"四道防线锁定存款与延迟赔付交易的绑定关系,并在支持/争议消息处理的最前端完成发送者身份认证。对于依赖过滤器、API 交易控制或争议处理路径的部署,本版本值得优先升级;而其沉淀的回归测试,也为后续过滤器实现的替换(新签名方案)提供了可验证的基线。
- 区块链
- DeFi
- 金融科技
【免费下载链接】bisq
A decentralized bitcoin exchange network
相关推荐
Crawl4AI v0.9.0 深度解析:默认安全加固的 Docker API 服务器与信任边界设计
Crawl4AI v0.9.0 深度解析:默认安全加固的 Docker API 服务器与信任边界设计 Crawl4AI v0.9.0 是其自托管 Docker
网页爬虫RAG人工智能AI 应用Apache APISIX 威胁模型解析:攻击面、信任边界与安全加固实践
Apache APISIX 威胁模型解析:攻击面、信任边界与安全加固实践 本文以 Apache APISIX 官方威胁模型(根目录 THREAT_MODEL.m
API网关后端云原生微服务LunaTranslator 视觉小说实时翻译完整指南:从第一次运行到进阶调优
LunaTranslator 视觉小说实时翻译完整指南:从第一次运行到进阶调优 LunaTranslator 是一款免费开源的视觉小说实时翻译工具:它从正在运行
桌面应用OCR人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考