
bitcore-lib 交易签名与序列化深度解析fee 计算、locktime 与 5 大检查项【免费下载链接】bitcore-libA pure and powerful JavaScript Bitcoin library项目地址: https://gitcode.com/gh_mirrors/bi/bitcore-libbitcore-lib是一个纯 JavaScript 编写的强大比特币Bitcoin库。本文将带你深入其交易签名与序列化的核心机制弄懂fee 计算的三套常量与估算公式、掌握locktime时间锁的两种形态并拆解serialize()序列化前自动执行的5 大检查项以及每一项对应的关闭开关——这是编写比特币钱包与交易工具时最该吃透的一课。一、一次签名 → 序列化的完整旅程在 bitcore-lib 中构建一笔可上链交易通常遵循这样一条链式调用源码位于 lib/transaction/transaction.jsvar transaction new Transaction() .from(utxos) // 喂入可用的未花费输出UTXO .to(address, amount)// 添加收款输出 .change(address) // 设置找零地址fee 从这里扣 .sign(privateKey) // 签名所有可签的输入随后用transaction.serialize()得到十六进制串即可广播到网络。但serialize()与toString()有本质区别方法行为toString()/uncheckedSerialize()直接输出不做任何检查serialize()先执行getSerializationError()的 5 大检查任一失败即抛错也就是说签名保证这交易是我签的序列化检查保证这交易是安全的。下面先讲检查项再讲 fee 与 locktime。二、序列化前的 5 大检查项serialize()调用getSerializationError()见 transaction.js按顺序执行以下检查命中即抛出对应错误#检查项触发条件可关闭关闭开关1输出金额非法任一输出satoshis 0或超过 2100 万 BTC❌ 不可跳过—2输出总和 输入总和未花费价值 0✅disableMoreOutputThanInput3fee 与设置值不一致已fee(n)但实际找零差额 ≠n❌ 不可跳过—4fee 过大 / 过小超出估算 fee 的上下限区间✅disableLargeFees/disableSmallFees5粉尘输出某输出 546sat 且非OP_RETURN✅disableDustOutputs6签名不完整isFullySigned()为 false✅disableIsFullySigned 第 2、4、5、6 项都可以通过serialize(unsafe)传对象精确关闭例如serialize({ disableSmallFees: true })传true或{ disableAll: true }则全部跳过等价于toString()。为什么费过大会特别检查找零地址第 4 项中若 fee 远超上限估算fee × 150且没有设置找零地址库会抛出更精确的ChangeAddressMissing错误——提示你手续费大得离谱而且没给找零地址这通常是忘了调用.change()导致的资损 bug。第 1 项为什么不可关闭输出金额非法负数或超过MAX_MONEY 21000000 × 1e8satoshi属于结构性错误这类交易在网络中必然被拒绝所以不提供关闭开关。三、fee 计算三套常量与一条估算公式fee 是输入总和 − 输出总和的差额归区块打包的矿工所有。bitcore-lib 在 transaction.js 顶部定义了四组关键常量常量值含义Transaction.FEE_PER_KB100000每 KB 计费单价satoshiTransaction.FEE_SECURITY_MARGIN150费用安全检查的容错倍数Transaction.DUST_AMOUNT546粉尘输出阈值satoshiTransaction.CHANGE_OUTPUT_MAX_SIZE62字节找零输出脚本的大小上界getFee() 的判定顺序getFee()见 transaction.js按以下优先级返回Coinbase 交易→ 返回0手动设置过fee(n)→ 返回n没设找零地址→ 返回输入−输出的全部差额差额全当手续费设了找零地址→ 走大小估算公式估算公式按交易大小按 KB 计费Transaction._estimateFee()的核心逻辑估算大小 4 9 9 4 // MAXIMUM_EXTRA_SIZE版本号、varint、锁时间等 Σ 每个输入的大小 Σ 每个输出 (脚本长度 9) fee ceil(估算大小 / 1000) × feePerKb如果扣除 fee 后剩余为正即会有找零输出还会把CHANGE_OUTPUT_MAX_SIZE62 字节加回大小再算一次——因为存在找零输出本身会让交易变长、fee 变高这个自洽修正避免了算了两次才对的问题。算例一笔 2 输入 2 输出的标准 P2PKH 交易签名后约 224 字节基础大小 ≈26 2×148 2×(259) 392字节 → 但脚本长度取实际值ceil(224/1000) × 100000 100,000satoshi≈ 0.001 BTC所以默认费率下任何 1KB 的交易都收 100,000 satoshi超过 1KB 则按整 KB 递增。你可用feePerKb(amount)覆盖单价以适配链上拥堵。检查区间150 倍容错序列化时的 fee 上下限由FEE_SECURITY_MARGIN 150推导见 transaction.js上限估算fee × 150超出即FeeError.TooLarge下限ceil(估算fee / 150)低于即FeeError.TooSmall以估算 fee 100,000 为例下限 ≈ 667 satoshi上限 15,000,000 satoshi。这个宽区间是为了允许用户自定义费率同时拦住fee 少算一个数量级或fee 大到送钱的笔误。⚠️ 注意官方文档 docs/transaction.md 中写的是FEE_PER_KB: 10000、FEE_SECURITY_MARGIN: 15与源码实际值100000 / 150不一致请以源码为准。四、locktime让交易未来才有效每笔交易尾部都有一个 32 位nLockTime字段决定交易最早何时可被打包。bitcore-lib 提供三个方法方法作用边界校验lockUntilDate(time)按 UNIX 时间戳锁定数值必须 ≥5e8否则抛LockTimeTooEarlylockUntilBlockHeight(height)按区块高度锁定数值必须 5e8且 ≥ 0getLockTime()语义化读取0→null5e8→区块高度否则→Date对象—5 亿是分界线NLOCKTIME_BLOCKHEIGHT_LIMIT 500000000小于它解释为区块高度大于它解释为秒级时间戳。用getLockTime()读回来时会自动还原成Date或数字避免你手动判断。隐藏细节sequence 号被悄悄改写两个lockUntil*方法内部都会把每个sequenceNumber 0xffffffff的输入改写为0xfffffffeDEFAULT_LOCKTIME_SEQNUMBER见 input.js。原因按比特币规则只有当至少一个输入的 sequence 0xffffffff 时nLockTime 才生效。库帮你自动做了这件事否则你的时间锁会静默失效。var future new Date(2026, 10, 30); transaction.lockUntilDate(future); console.log(transaction.getLockTime()); // → Date 对象五、签名机制sighash 如何只锁住该锁的签名核心在 sighash.js流程是sign → sighash → ECDSA.signTransaction.shallowCopy()克隆一笔影子交易清空所有其他输入的脚本只保留当前输入对应的子脚本subscript按 sighash 类型裁剪SIGHASH_NONE (0x02)→ 清空全部输出其他输入 sequence 置 0SIGHASH_SINGLE (0x03)→ 输出截断到inputNumber1个含著名的SIGHASH_SINGLE历史 buginputNumber ≥ 输出数时直接返回固定哈希SIGHASH_ANYONECANPAY (0x80)→ 输入只保留当前这一个拼接影子交易序列化 sighashType(4字节)做SHA256d得到待签哈希这样设计的好处SIGHASH_ALL默认锁定全部输入全部输出改动任何一处都会让签名失效资金安全而SIGHASH_SINGLE|SIGHASH_ANYONECANPAY组合可签名只承诺这一个输入花到这个输出用于部分签名协作如多签分签。sighash 类型常量定义在 lib/crypto/signature.jsSIGHASH_ALL 0x01SIGHASH_NONE 0x02SIGHASH_SINGLE 0x03SIGHASH_ANYONECANPAY 0x80验签时verify()用相同流程重算哈希再ECDSA.verify多签场景下用transaction.isValidSignature(sig)applySignature(sig)可把对方算好的签名贴回交易最终isFullySigned()为true即可序列化。六、实战一笔安全交易的自检清单把前文串起来广播前建议确认inputAmount≥outputAmount否则检查项 2 失败调过.change(address)且找零金额 DUST_AMOUNT (546)否则检查项 5 失败getFee()返回值在预期区间费率不合时宜时用feePerKb(n)调整需要时间锁时调了lockUntilDate/lockUntilBlockHeight并确认 sequence 已被改写多签场景isFullySigned()为true否则检查项 6 失败最后serialize()未抛错 → 拿 hex 串广播七、相关文件索引模块相对路径职责交易主体lib/transaction/transaction.jsfee、locktime、序列化检查签名哈希lib/transaction/sighash.jssighash 计算与验签输入基类lib/transaction/input/input.jssequence 常量、大小估算输入索引lib/transaction/input/index.jsP2PKH / P2SH 多签输入分发sighash 常量lib/crypto/signature.jsSIGHASH_* 位标志官方文档docs/transaction.md事务 API 总览序列化测试test/transaction/transaction.jsfee / 检查项的回归用例一句话总结fee 按大小 × 单价估算并受 150 倍安全区间约束locktime 用 5 亿分界区块高度与时间戳并自动改写 sequence而serialize()的 5 大检查项金额非法、输出超输入、fee 不一致、fee 越界、粉尘/缺签是防止误广播的最后一道闸门——吃透这三块你就掌握了 bitcore-lib 交易模块 90% 的安全细节。【免费下载链接】bitcore-libA pure and powerful JavaScript Bitcoin library项目地址: https://gitcode.com/gh_mirrors/bi/bitcore-lib创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考