- 区块链
- DeFi
- 金融科技
【免费下载链接】bisq
A decentralized bitcoin exchange network
Bisq 1.10.3 是一次典型的安全加固(security-hardening)版本:它在 10 个提交中集中处理了未签名 Filter 字段、Core API 失败交易状态迁移、Deposit/DPT 交易绑定校验以及争议消息发件人认证四大类风险点。本文基于当前仓库中的 1.10.3 完整发布说明,逐项还原其兼容性影响与实现细节,并结合FilterManager、CoreTradesService、BuyerSetupDepositTxListener、SupportManager等源码与回归测试,说明每项加固在 Bisq 代码库中具体落在哪里、如何验证。
版本概况与提交清单
1.10.3 的发布说明采用与 Bitcoin Core 发布说明一致的务实风格:用户与运维影响在前,完整可审计的提交清单在后。提交区间为358b546526d07445a857aa70da76cc1acdeca53b(含)至292f438112b3c721a1a73ff2eb8c3ed8ac7fdce6,由以下命令生成(区间含起始提交,覆盖标签v1.10.3的全部后续提交):
git log --reverse 358b546526d07445a857aa70da76cc1acdeca53b^..v1.10.3统计信息(摘自 full-release-notes.md):
- Total commits:10
- Non-merge commits:7
- Merge commits:3
- Files changed:27
- Generated on:2026-07-06
按git log --reverse顺序排列的完整提交清单如下(作者与日期以发布说明为准):
| 日期 | 提交 | 类型 | 摘要 | 作者 |
|---|---|---|---|---|
| 2026-07-04 | 358b546526 | Commit | 停用 filter 提供的 BTC 节点与种子节点的持久化 | HenrikJannsen |
| 2026-07-04 | ca462d1f48 | Commit | 临时停用 filter 提供的 BTC 费用接收地址 | HenrikJannsen |
| 2026-07-04 | e6ecc74f6e | Commit | 阻止失败交易经由 API 进入 payment-started 状态 | HenrikJannsen |
| 2026-07-04 | 8aeddc7bb5 | Commit | 忽略 filter 提供的 BTC 节点与种子节点的配置值 | HenrikJannsen |
| 2026-07-05 | 9d2c952555 | Merge | 合并未签名 filter 种子/BTC 节点停用 | Alejandro Garcia |
| 2026-07-05 | 802ab0e129 | Merge | 合并 BTC 费用接收地址 filter 停用 | Alejandro Garcia |
| 2026-07-05 | c7b68d485f | Merge | 合并失败交易 payment-start API 守卫 | Alejandro Garcia |
| 2026-07-05 | 7a441aa6fc | Commit | 版本号提升至v1.10.3 | Alejandro Garcia |
| 2026-07-05 | 7e7e4ed905 | Commit | 加固 Deposit 交易与 Delayed Payout 交易绑定 | Alejandro Garcia |
| 2026-07-05 | 292f438112 | Commit | 加固支持消息与争议消息的发件人认证 | Alejandro Garcia |
兼容性影响:升级者需要知道什么
发布说明列出的 7 条兼容性要点,是运维升级前的核心检查清单:
- Bisq 版本被设置为
1.10.3; - Filter 提供的附加 BTC 节点与种子节点被禁用,因为这些字段未包含在当前 filter 签名范围内;
- 之前已持久化的
filterProvidedBtcNodes、filterProvidedSeedNodes配置值在本版本中被忽略; - Filter 提供的 BTC 费用接收地址被临时忽略,但 mempool 费用接收列表仍包含 DAO 捐赠地址与 Burning Man 接收地址;
- Core API 现在拒绝为已标记为失败的交易确认付款开始(payment-start);
- Deposit 交易与 Delayed Payout 交易的处理在变更钱包或交易状态之前执行更严格的绑定校验;
- 当发件人签名公钥不匹配预期的 peer、trader 或争议代理时,交易聊天、ACK、发起争议、peer 发起争议、争议结果五类消息一律被忽略。
发布说明明确建议:这是一个安全加固版本,应尽快升级,尤其是依赖 filter、API 交易控制或争议消息处理路径的节点。
未签名 Filter 字段停用(Unsigned Filter Fields Disabled)
背景:Filter 是签名策略,未签名字段不可信
Bisq 的应急策略体系由两部分组成:P2P 分发的签名网络Filter,以及启动时加载的静态DenyList(详见 filter-and-deny-list.md)。签名 filter 由受信任的开发者密钥发布,FilterManager会拒绝验签失败的 filter、拒绝时间过远的 filter,并在多个有效 filter 中优先采用创建时间最新者。从源码结构看,受信任的开发者公钥硬编码在 FilterManager 构造函数中(非 dev 模式下为 3 个公钥),这决定了"哪些字段真正受签名保护"的边界。
问题在于:Filter对象中携带的"附加 BTC 节点"和"附加种子节点"字段位于当前 filter 签名的覆盖范围之外。这意味着攻击者若能篡改这两个字段,节点会据此连上恶意节点而不会触发验签失败。1.10.3 的处理方式是:与其信任,不如彻底停用。
源码层面的落点
- 停用持久化(提交
358b546526):FilterManager不再把 filter 提供的 BTC 节点与种子节点写回本地选项文件。这两个字段在 Config 中分别定义为filterProvidedBtcNodes与filterProvidedSeedNodes(常量FILTER_PROVIDED_BTC_NODES/FILTER_PROVIDED_SEED_NODES),并在 Config 中暴露为List<String>字段。 - 忽略已持久化值(提交
8aeddc7bb5):Config对这两个已持久化的键直接忽略,防止旧版本写入的陈旧值在升级后启动时生效。这与 filter-and-deny-list.md 中"这些持久化值是 filter 状态而非用户偏好"的描述一致——它们是 filter 派生状态,而非运维显式配置的bannedSeedNodes/bannedBtcNodes。 - 种子节点侧的消费点:在 DefaultSeedNodeRepository 中可以看到
config.filterProvidedSeedNodes原本是种子节点缓存的来源之一,并且受--ignoreNetworkFilter开关控制。1.10.3 之后,该来源在 filter 签名方案重写("replacement filter implementation")可用之前被视为不安全,按空值写入与生效。
相关测试覆盖了这条路径,包括 FilterManagerInitializationTests、FederatedBtcNodeProviderTest、BtcNodesSetupPreferencesTest 与 DefaultSeedNodeRepositoryTest。
临时停用 Filter 提供的 BTC 费用接收地址
Filter 提供的btcFeeReceiverAddresses同样被临时排除在 mempool 服务的费用接收地址列表之外。发布说明特别说明:该路径当时未被主动使用("The path was not actively used"),预期由新的 filter 实现接管;同时BtcFeeReceiverService.java本身没有被修改,行为变化发生在MempoolService。
从当前代码树看 MempoolService.getAllBtcFeeReceivers,完整的 BTC 费用接收地址列表由四部分合并而成:
- DAO 捐赠地址(
daoFacade.getAllDonationAddresses()); - Burning Man 地址列表的 legacy 地址(
addBurningManAddressListLegacyAddress); - 经 BtcFeeReceiverService.getConfiguredReceiverAddresses 解析、来自 filter 的地址(经由 FilterPolicyService.getBtcFeeReceiverAddresses 读取当前生效 filter 的
btcFeeReceiverAddresses字段); - 分布式 Burning Man 地址。
需要说明的适用前提:当前仓库Version已演进到1.10.9(见 Version.java),上表反映的是当前代码树的构造方式;在 1.10.3 时点,第 3 类 filter 来源按发布说明被临时排除,仅第 1、2(、4)类地址进入 mempool 校验,用于parseJsonValidateMakerFeeTx/parseJsonValidateTakerFeeTx等费用交易 JSON 校验(MempoolService 中可见调用点)。
失败交易的 API 守卫(Failed Trade API Guard)
CoreTradesService.confirmPaymentStarted新增一条前置检查:如果交易已被标记为失败,直接抛出异常,阻止 API 客户端把失败交易沿着买方协议路径推进到 payment-started 状态。入口是 CoreApi.confirmPaymentStarted(接受tradeId及可选的txId、txKey),实现位于 CoreTradesService:
void confirmPaymentStarted(String tradeId, @Nullable String txId, @Nullable String txKey) { var trade = getTrade(tradeId); if (isFollowingBuyerProtocol(trade)) { if (trade.hasFailed()) { throw new FailedPreconditionException("Cannot confirm payment started for a failed trade."); } ... } }同一守卫之后还有两道既有检查可对照理解:存款交易未确认(!trade.isDepositConfirmed())时同样拒绝;卖方付款账户载荷缺失时也拒绝,因为"买方没有收款细节,付款开始消息等于宣称了一笔不可能发生的付款"。这个守卫与争议流程直接相关——失败交易(FailedTradesManager 管理的交易)之后仍可能进入争议,因此 API 层不能允许它"复活"。
Deposit 交易与 Delayed Payout 交易的绑定加固
这是 1.10.3 中技术含量最高的一组变更(提交7e7e4ed905),核心思路是:所有校验必须在变更钱包或交易状态之前完成,且必须验证交易之间的密码学绑定关系,而不是只做字段级检查。
买方侧处理DepositTxAndDelayedPayoutTxMessage
在 Bisq 交易协议中,卖方通过 DepositTxAndDelayedPayoutTxMessage 把 deposit 交易与 delayed payout 交易(DPT)一起发给买方,买方协议在 BuyerProtocol 中转入BuyerProcessDepositTxAndDelayedPayoutTxMessage任务。1.10.3 之后,买方侧校验包含三层:
- peer deposit 交易与本地 deposit 交易一致性:买方先验证对方发来的 deposit 交易与自己本地的 deposit 交易匹配,然后才允许变更钱包或交易状态。对于 buyer-as-maker 交易,由于 maker 此时尚未设置 deposit 交易,校验对象改用已准备的(prepared)deposit 交易;
- DPT 必须花费 peer deposit 交易的第 0 个输出:DPT 与 peer deposit 交易做绑定校验,防止接受一笔"序列化正确、但实际绑定到另一笔 deposit 交易"的 DPT;
- 一切拒绝都发生在状态变更前(fail before mutation)。
回归测试 BuyerProcessDepositTxAndDelayedPayoutTxMessageTest 的测试方法名与发布说明逐条对应:
runAcceptsDelayedPayoutTxWithNonZeroLockTime—— 合法 DPT(含非零 lockTime)被接受;runUsesPreparedDepositTxForBuyerAsMaker—— buyer-as-maker 使用 prepared deposit 交易;runRejectsMismatchedDelayedPayoutTxBeforeWalletMutation—— DPT 不匹配时在钱包变更前拒绝;runRejectsPeerDepositTxNotBoundToBuyersDepositTxBeforeWalletMutation—— 对方 deposit 交易未绑定到买方本地 deposit 交易时拒绝;runRejectsPeerDelayedPayoutTxNotSpendingDepositTxOutputZeroBeforeWalletMutation—— DPT 输入未花费 deposit 交易 output 0 时拒绝。
存款确认回调绑定到注册时的交易 ID
Trade.setupConfidenceListener 在注册确认监听时把回调绑定到当时 deposit 交易的 txid。注释直接说明了动机:"deposit 交易可能被覆盖(例如网络观察到的交易被最终的 deposit/DPT 消息替换),来自已不是当前交易的陈旧 confidence 回调绝不能把交易标记为 deposit-confirmed"。回调触发时 setConfirmedState 先比对 txid,不一致则记录Ignoring stale deposit-tx confirmation...并忽略,只有确认通过且状态未推进过时才置为DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN。
maker-fee / taker-fee 输入必须"各自不同"
BuyerSetupDepositTxListener 的存款交易判定逻辑从"匹配输入数量 ≥ 2"升级为"必须各有一个不同的 maker-fee 输入和一个不同的 taker-fee 输入"。源码注释给出了具体攻击场景:
We must require one distinct maker-fee input AND one distinct taker-fee input, not just a count of inputs whose parent txid is one of the two fee txids. Otherwise, a malicious seller-as-maker could satisfy a count==2 check using two different outputs of his own maker fee tx (the maker fee tx can have fee, reserved-for-trade and change outputs), and get us to accept a fake deposit tx that was never co-funded by the taker.
也就是说,恶意卖方可以利用 maker fee 交易自带的多个输出(fee、reserved-for-trade、change),用同一笔 fee 交易的不同输出凑出"2 个匹配输入",骗过旧的计数检查,使买方接受一笔从未经 taker 共同出资的假 deposit 交易。新逻辑按outpoint父交易 ID 分别置位hasMakerFeeInput/hasTakerFeeInput两个布尔量;当takerFeeTxId未知(buyer-as-maker 场景)时只要求 maker-fee 输入存在。该校验还叠在既有的防地址复用逻辑之上(同文件中注释说明:历史 bug 曾导致 Multisig 地址条目被重复使用,使同一depositTxAddress出现在多笔交易中)。
支持消息与争议消息的发件人认证
提交292f438112将"解密后的发件人签名公钥"贯穿到直接消息与邮箱消息的完整处理链,并对五类消息做发件人绑定校验。
交易聊天与 ACK
SupportManager.onChatMessage 在处理聊天消息前,先用本地交易上下文解析预期的 peer 公钥环(getPeerPubKeyRing),再调用 isSenderSignaturePubKeyExpected 比对解密出的发件人签名公钥;不匹配则记录sender signature pubKey does not match the expected peer并直接丢弃,不写入聊天、不回复 ACK。
onAckMessage 的加固更为关键:ACK 消息不再从不可信的消息体数据中解析 peer 公钥,而是要求先在本地聊天记录中找到 ACK 所指的源消息(按sourceId+sourceUid检索);找不到源消息直接忽略并告警,找到后才从源消息取预期公钥环做发件人校验。
争议发起、peer 发起争议与争议结果
DisputeManager 在两条关键路径上做发件人绑定:
- 发起争议(open dispute):校验签名发件人与内嵌合同(contract)中声明的争议发起方一致,并拒绝 trader 公钥与声明发起方不一致的争议;
- peer 发起争议 / 争议结果(dispute-result):以本地交易记录中预期的 mediation、refund 或 arbitration 代理公钥为准校验发件人,并拒绝载荷中的代理公钥与本地交易不匹配的数据。
此外,无效争议数据现在在争议列表被变更或持久化之前被拒绝(配合 DisputeValidation 的数据校验路径),避免脏数据进入持久层。
争议认证回归测试位于 DisputeManagerAuthTest 与 MediationManagerAuthTest。前者中几个值得注意的用例直接对应发布说明的 "failed-trade lookup during dispute authentication":
findTradeIncludesFailedTrades—— 交易查找必须包含失败交易(否则失败交易上的争议无法通过认证);agentSignatureAcceptsFailedTradeAgentKeyFromLocalTrade—— 接受来自本地交易记录的失败交易代理公钥;agentSignatureRejectsFailedTradeWhenDisputeAgentKeyDoesNotMatchLocalTrade—— 代理公钥与本地交易不匹配时拒绝。
版本发布与回归验证
- 版本提升:应用与打包版本统一提升到
1.10.3(提交7a441aa6fc);v1.10.3标签包含版本号提升以及其后的 deposit/DPT、争议消息加固提交。当前仓库的版本常量见 Version.java(HEAD 已演进至后续版本,本文结论以 1.10.3 发布说明为准)。 - 回归覆盖(摘自发布说明):DPT 输入绑定、deposit 交易不匹配拒绝、buyer-as-maker deposit 处理、陈旧存款确认处理、支持 ACK 认证、争议发起方认证、争议代理认证、争议认证期间的失败交易查找、mediation 争议结果发件人认证。
- 发布门禁(摘自 notable-changes.md):作为安全加固版本,发布验证应包含 filter/config 路径、Core API 失败交易守卫、deposit/DPT 绑定、支持/争议消息认证四类聚焦测试。
小结
1.10.3 的改动看似分散,实际共享同一条设计原则:任何来自网络的字段、交易或消息,在触发本地状态变更(持久化、钱包、交易状态机)之前,必须先通过与"本地可信上下文"的绑定校验——未签名的 filter 字段直接停用,费用接收地址收窄到可信来源,失败交易被 API 层拦截,deposit/DPT 用密码学绑定关系(output 0 花费、fee 输入来源、txid 回调绑定)而非形状检查来防伪,聊天与争议消息一律以本地交易记录中的公钥为唯一认证基准。对于运行依赖 filter、API 交易控制或争议流程的节点,理解这五条路径既是对升级必要性的解释,也是一份可对照当前源码复核安全属性的检查清单。
- 区块链
- DeFi
- 金融科技
【免费下载链接】bisq
A decentralized bitcoin exchange network
相关推荐
Bisq 1.10.1 发布说明深度解读:BSQ swap 崩溃修复、费用钳制与交易限额调整
Bisq 1.10.1 发布说明深度解读:BSQ swap 崩溃修复、费用钳制与交易限额调整 本篇基于 Bisq 仓库中的 1.10.1 完整发布说明 http
区块链DeFi金融科技Bisq 1.10.8 安全加固版本解读:退款赔付校验、交易验证与 DAO 完整性防线
Bisq 1.10.8 安全加固版本解读:退款赔付校验、交易验证与 DAO 完整性防线 Bisq 1.10.8 是一个以安全为核心目标的版本,重点覆盖退款赔付(
区块链DeFi金融科技EOSIO v2.1.0-rc3 发布说明解读:安全加固、稳定性修复与 2.1 版本系列全景
EOSIO v2.1.0 rc3 发布说明解读:安全加固、稳定性修复与 2.1 版本系列全景 本篇技术指南以 EOSIO 官方 v2.1.0 rc3 发布说明(
区块链
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考