资讯动态

Bitcoin Core 包式内存池准入(Package Mempool Accept):规则、费用模型与源码实现

发布时间:2026/9/7 4:27:31 来源:尧图企业网站定制
Bitcoin Core 包式内存池准入Package Mempool Accept规则、费用模型与源码实现【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin本文基于 Bitcoin Core 仓库的策略文档 doc/policy/packages.md系统讲解包式内存池准入Package Mempool Accept机制什么是包package、子-父child-with-parents拓扑、包级大小与权重限制、当前受限的包替换Package RBF规则、去重deduplication策略以及包级费率package feerate如何帮助低费率父交易在内存池拥挤时仍然被接受。文末结合 src/validation.cpp 与 src/rpc/mempool.cpp 的源码给出submitpackage等 RPC 的完整参数说明与真实验证流程帮助开发者理解该机制的设计动机与实现细节。一、核心概念什么是包Package按 doc/policy/packages.md 的定义包package一个有序的交易列表可用有向无环图DAG表示——当一个交易花费了另一个交易的输出时两者之间存在一条有向边。拓扑有序topologically sorted对于包中每个交易t如果它的父交易即被它花费输出来源的交易也在包中那么这些父交易必须出现在列表中t的前面。子-父包child-with-parents package一个拓扑有序的包由恰好一个子交易和至少一个未确认的父交易组成。父交易不必全部到场但也不允许出现包中其他交易父交易的父祖父不能出现在包中除非这个祖父同时也是子交易的直接父。这个拓扑约束是整个机制的关键它把准入逻辑限制在一个子交易 若干父交易的可推理范围内。1.1 定义在源码中的落点这些概念在 src/policy/packages.h 中有直接对应包被定义为using Package std::vectorCTransactionRefpackages.h#L45即交易不能互相冲突花费相同输入的有序列表。IsTopoSortedPackage()检查所有父交易是否都排在其子交易之前其注释也说明该函数无法检测间接依赖父不在包中时无法发现祖父的排序问题见 packages.h#L49-L55。IsConsistentPackage()检查包内交易是否花费相同的 prevout输入冲突同时把空vin的交易视为不一致见 packages.h#L57-L66。IsChildWithParents()上下文无关地检查包是否为最后一个交易是子交易、其余交易都是它的父交易IsChildWithParentsTree()进一步要求父交易之间互不依赖包是树形见 packages.h#L76-L85。二、对所有包生效的准入规则以下规则对所有包包括实际提交和 test accept都强制执行源自 doc/policy/packages.md2.1 数量与总权重上限#20833包内交易数量不得超过MAX_PACKAGE_COUNT25包内交易总权重不得超过MAX_PACKAGE_WEIGHT404000。设计理由原文希望包尽量小以缓解通过包验证发起的 DoS 攻击同时又保证该限制不会排除那些如果逐个单独提交就会被接受的祖先包。文档还特别提醒如果内存池的容量限制发生变化包的限制也需要重新考虑用户也可能自行配置不同的内存池限制。注意这里用的是**交易权重weight**而不是其他限制常用的虚拟大小vsize目的是让检查可以脱离上下文完成。源码中这两个常量定义在 src/policy/packages.h#L18-L30/** Default maximum number of transactions in a package. */ inline constexpr uint32_t MAX_PACKAGE_COUNT{25}; inline constexpr uint32_t MAX_PACKAGE_WEIGHT 404000; static_assert(MAX_PACKAGE_WEIGHT MAX_STANDARD_TX_WEIGHT);从源码结构看头文件里还有一组static_assert把包级限制与内存池的簇cluster限制绑定在一起DEFAULT_CLUSTER_LIMIT MAX_PACKAGE_COUNT、MAX_PACKAGE_WEIGHT DEFAULT_CLUSTER_SIZE_LIMIT_KVB * WITNESS_SCALE_FACTOR * 1000因为一个包属于同一个簇——包限制必须落在簇容量之内才自洽。单交易场景下MAX_PACKAGE_WEIGHT MAX_STANDARD_TX_WEIGHT保证包限制不会比单交易标准限制更苛刻。2.2 拓扑有序、无冲突、无重复文档要求的三条结构性规则包必须拓扑有序#20833包内不能有冲突交易即包中任意两个交易不能花费相同的输入包内也不能有重复交易#20833只考虑有限的包替换见下节#28984。这三条由IsWellFormedPackage()集中实现见 src/policy/packages.cpp#L79-L117。它的检查顺序和错误码对排查问题很有用检查项错误码说明package_count MAX_PACKAGE_COUNTpackage-too-many-transactions交易数超过 25多交易且total_weight MAX_PACKAGE_WEIGHTpackage-too-large单交易超重大时优先报单交易的权重策略错误package_count 1才检查见 packages.cpp#L89-L92later_txids.size() ! txns.size()package-contains-duplicates按 txid 去重计数同时覆盖重复 wtxid 和同 txid 不同 witness的交易IsTopoSortedPackage失败package-not-sorted父交易排在其子交易之后IsConsistentPackage失败conflict-in-package两个交易花费同一 prevout实现上有个小细节IsTopoSortedPackage通过维护一个当前交易及之后所有交易的 txid 集合来做检查——若某交易的输入落在该集合中说明它的父交易排在它后面直接判为未排序packages.cpp#L17-L41。注释里还解释了为什么优先报未排序而不是等它报缺输入未排序的包最终也会因 missing-inputs 失败但提前失败在语义上更明确缺输入也可能是孤儿交易或试图花费不存在的币。而IsConsistentPackage采用整笔交易的输入批量入集合的方式避免把单个交易内部重复花费同一输入这是更严重的共识级错误应由CheckTransaction()报告误判为包级冲突见 packages.cpp#L52-L77。上下文无关的检查数量、权重、拓扑、一致性可以在拿内存池锁之前完成这在 src/validation.cpp 的AcceptMultipleTransactionsInternal()开头被明确利用These context-free package limits can be done before taking the mempool lockvalidation.cpp#L1433-L1435。三、当前受限的包替换Package RBF规则文档指出#28984当前只考虑有限的包替换具体约束包必须是1 父 1 子1-parent-1-child结构且包内交易不能有任何内存池内的祖先含冲突交易的不同簇数量不得超过 100与常规 替换规则第 5 条 类比替换必须在增量中继费率incremental relay fee下支付更多总费用类比常规 替换规则第 3、4 条父交易的费率必须低于包费率必须改善费率图feerate diagram#29242。设计理由原文钱包可以用长度不超过两笔的交易链作为基本的包 RBF 支持在需要时直接让这样的链相互冲突再结合TRUC 交易一种特殊的替换场景可以实现更鲁棒的提费。未来可能开放更通用的包 RBF。从源码结构看AcceptMultipleTransactionsInternal()中会显式调用PackageRBFChecks()执行上述检查且代码注释说明了当前实现依赖1p1c 无内存池祖先这一前提来简化对内存池内被替换交易的追踪见 validation.cpp#L1463-L1472 与 validation.cpp#L1510-L1513TRUC 校验则通过PackageTRUCChecks()在每个 workspace 上运行validation.cpp#L1477-L1482。四、仅对实际提交到内存池的包生效的规则以下两条规则只在包真正提交submit到内存池时强制test accept 不强制4.1 必须是 child-with-parents 包#31096包必须是 child-with-parents 包这也意味着包至少包含 1 个交易。理由允许通过 CPFPChild Pays For Parent子付父方式提费允许多个父交易则可以对一批交易整体提费。把包限制在明确定义的结构上便于推理也大幅简化验证逻辑。警告原文批量提费在某些使用场景下可能不安全用户和应用程序开发者使用多父包时要谨慎。4.2 去重Deduplication包中若存在与内存池中已有交易txid 相同的交易会在提交前从包中移除。文档给出两条理由节点运营者可以自由设置自己的内存池策略各节点收到交易的顺序可能不同恶意对手可能利用策略差异来钉住pin或延迟交易传播。因此包中的部分交易有可能已经在内存池里对这些交易重复验证或在费用中重复计数既无必要也可能出错。防审查不应该因为内存池已有其中一笔交易就拒绝整个包。更进一步即使攻击者先广播了一个同 txid、不同 witness的冲突交易——这种交易既冲突又无法互相替换——诚实的包仍应被考虑接受。五、包费用与包费率Package Fees and Feerate5.1 定义包费率 包内所有交易的总修正费用基础费用 prioritisetransaction带来的费用增量÷ 包内所有交易的总虚拟大小。如果包中有些交易已经在内存池里它们会被去重、不再提交因此不参与这个计算。5.2 满足动态最低费率包内 CPFP要满足动态的内存池最低费率——即由内存池达到容量时被驱逐交易决定的费率而不是静态的最低中继费率——可以使用总包费率而不是单笔交易的费率。举例原文如果内存池最低费率是 5 sat/vB一笔 1 sat/vB 的父交易带着一个高费率子交易以包形式提交时可能被接受。理由原文这可以理解为包内 CPFP解决了预签名交易无法再签出更高费的替换交易在交易量高、内存池最低费率上升时被拒之门外的问题。5.3 实现细节逐笔先验失败再走包验证文档特别指出一条实现注记包内交易总是先被单独验证包验证只用于单独验证失败的交易。由于包费率只用内存池中不存在的交易计算这个实现细节会影响包验证的结果。理由有三条值得展开不能重复计费已经在内存池里的交易其费用不应再被计算一次。激励相容禁止父付子包是为激励相容的提费而设计的——交易 B 只有当且仅当 B 是 A 的后代且费率高于A 时才是 A 的合法提费。父交易的费用不应帮助子交易因为父交易可以不含子交易就被挖入区块。更一般地如果挖 A 不需要 BB 的费用就不能帮 A。在 child-with-parents 包里先单独验证父交易、剔除单独就达标的父交易即可保证这一点。子不能拖累父低费率子交易不应阻止父交易被接受挖父不需要子子的费用不应伤害父。在 child-with-parents 包里先单独验证父交易即可保证这一点。不意外收紧策略作为原则要避免为了与依赖 P2P 中继的用户/应用保持向后兼容而意外限制策略。具体地说包验证不应阻止任何本身策略有效的交易被接受总是先接受单独验证通过的交易、再尝试包验证就不会无意收紧策略。5.4 源码中的验证流水线MemPoolAccept::AcceptPackage()src/validation.cpp#L1618-L1767把上述规则落为一条清晰的处理链先做上下文无关检查IsWellFormedPackage()以及多交易包必须是 child-with-parents错误码package-not-child-with-parentsvalidation.cpp#L1632-L1641——这两步在拿内存池锁之前完成尽早快速失败逐笔遍历包内交易并分三种情况处理validation.cpp#L1654-L1713wtxid 已在内存池直接标记为已在内存池不重复验证同 txid 不同 wtxid 已在内存池当前不允许 witness 替换直接用内存池中的版本顶替代码中有TODO: allow witness replacement in packages都不在内存池先调用AcceptSubPackage({tx}, args)单独验证该交易。若单独通过直接入账且不再参与包验证费用只应被使用一次若单独失败且失败原因不是TX_RECONSIDERABLE费率类失败或TX_MISSING_INPUTS父交易可能是包内前面因费率失败的交易则标记quit_early不再做包验证——因为包验证与单独验证的差异只在费率评估共识层面的失败不会因为打包而改变。这个最小化重复工作的取舍在源码注释中有完整说明。剩余可挽救的交易费率失败或缺少包内父交易收集进txns_package_eval整体交给AcceptSubPackage()→AcceptMultipleTransactionsInternal()其中用包级聚合费率执行CheckFeeRate()validation.cpp#L1484-L1508注释同样强调聚合费率检查本身不保证父不为子买单子不低于最低费率这些要靠调用方限制拓扑如祖先集并分别检查个体与子集的费率。提交成功后调用LimitMempoolSize()强制内存池容量包交易包括本来就在池里的都可能因此被驱逐驱逐后会以mempool full返回失败validation.cpp#L1724-L1766。AcceptMultipleTransactionsInternal()内部还有一个值得注意的机制包内每笔交易验证时都通过m_viewmempool.PackageAddTransaction()把该交易产出的币临时放入可用 UTXO 视图让后一笔交易能花费前一笔的输出validation.cpp#L1463-L1473。验证结束后由CleanupTemporaryCoins()清理这些临时币和内存池币避免后续调用误用不存在的输出validation.cpp#L1562-L1590。入口函数ProcessNewPackage()根据test_accept标志区分两条路径测试接受走AcceptMultipleTransactionsAndCleanup()对整包做包验证、不去重实际提交走AcceptPackage()含去重与逐笔先验见 validation.cpp#L1802-L1819。六、RPC 实操submitpackage 与 testmempoolaccept包准入对用户可见的两个入口都在 src/rpc/mempool.cpp 中。6.1submitpackage实际提交示例来自 RPC 帮助文本mempool.cpp#L1399-L1402submitpackage [raw-parent-tx-1, raw-parent-tx-2, raw-child-tx]参数参数类型/默认值说明rawtxs数组必填十六进制原始交易数组1MAX_PACKAGE_COUNT25笔父子必须父在前maxfeerate金额默认DEFAULT_MAX_RAW_TX_FEE_RATE超过该费率BTC/kvB的交易会被拒大于 1 BTC/kvB 的费率值本身会被拒设为 0 表示不做该检查mempool.cpp#L1361-L1363maxburnamount金额默认DEFAULT_MAX_BURN_AMOUNT拒绝携带超过该数额的不可花费输出如 OP_RETURN 数据输出基于启发式不保证可花费性mempool.cpp#L1364-L1368返回值mempool.cpp#L1370-L1398package_msg包级结果消息success表示所有交易都已进入内存池或本来就在内存池tx-results按 wtxid 为键的逐笔结果每笔必有一项包含txid、other-wtxid内存池中存在同 txid 不同 witness 的交易说明提交的这笔被忽略、vsize_adjusted/vsize已弃用实为 sigops 调整后的 vsize/vsize_bip141、feesbase、effective-feerate、effective-includes——后者列出哪些交易的费率和大小被计入了有效费率即包费率体现处、error被拒原因或package-not-validated表示包在逐笔处理前就中止replaced-transactions被替换掉的交易 txid 列表。注意submitpackage的包级限制检查数组为空或超过 25 笔直接抛RPC_INVALID_PARAMETERmempool.cpp#L1405-L1409。6.2testmempoolaccept仅测试多笔传入时同样适用包策略父交易必须在前、交易之间以及与内存池已有交易不能冲突单笔失败时其他交易可能不会被完整验证allowed键为空。最大交易数同样是MAX_PACKAGE_COUNT25见 mempool.cpp#L284-L300。与submitpackage的差别testmempoolaccept不允许包内交易已存在于内存池源码中有CHECK_NONFATAL(tx_result.m_result_type ! MempoolAcceptResult::ResultType::MEMPOOL_ENTRY)mempool.cpp#L393-L394即它不做去重路径这正对应文档所说child-with-parents 与去重两条规则只在实际提交时强制。七、测试用例与延伸阅读仓库内的测试覆盖了对应文档中的每条规则test/functional/rpc_packages.pysubmitpackage/testmempoolaccept的综合功能测试覆盖拓扑、冲突、去重、费用计算等test/functional/mempool_package_rbf.py1p1c 包替换规则的边界行为test/functional/mempool_truc.pyTRUC 交易场景test/functional/mempool_cluster.py簇cluster限制与包限制的交互test/functional/wallet_anchor.py钱包对 child-with-parents 提费的锚定用法src/test/txpackage_tests.cpp单元测试直接围绕MAX_PACKAGE_COUNT、MAX_PACKAGE_WEIGHT构造超限包验证拒绝路径txpackage_tests.cpp#L138-L156。同目录下的 doc/policy/mempool-design.md、doc/policy/mempool-terminology.md、doc/policy/mempool-replacements.md 分别解释了内存池整体设计、术语含 TRUC以及常规 RBF 规则是理解本文规则的上下文文档。八、小结包准入的本质是用严格受限的拓扑child-with-parents 上下文无关的结构检查≤25 笔、≤404000 权重、拓扑有序、无冲突无重复 包级聚合费率让低费率父 高费率子这类预签名交易组合在内存池高水位时仍有机会进入内存池同时通过先逐笔、后打包的验证顺序杜绝父为子买单、子拖累父、费用重复计数等激励不相容问题所有常量与检查函数集中在 src/policy/packages.h 与 src/policy/packages.cpp主流程在 src/validation.cpp 的MemPoolAccept中RPC 入口在 src/rpc/mempool.cpp需要牢记的前提这些是当前仓库实现的策略policy而非共识consensus规则各节点可以自行配置内存池相关参数文档也明确提示包级限制与内存池容量限制联动调整未来可能扩展更通用的包 RBF。【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价