比特币交易签名实战:token-core-android 的 UTXO 模型、找零与多输入签名
【免费下载链接】token-core-androida blockchain private key management library on android项目地址: https://gitcode.com/gh_mirrors/to/token-core-android
比特币交易签名与以太坊式的"扣余额"完全不同:它要做的不是减少账户数字,而是把历史 UTXO 一枚枚花掉。token-core-android 是一款面向 Android 平台的区块链私钥管理库,它把 UTXO 模型、找零输出与多输入签名封装成统一且简洁的 API。本文以BitcoinTransaction为切入点,用最通俗的语言拆解比特币交易签名的完整流程,帮你快速上手,避开新手常见的大坑。
为什么说比特币交易签名是"花硬币"而不是"扣余额"?🧐
比特币里根本没有"账户余额"这个概念。每个地址拥有的,是一堆未花费的交易输出(Unspent Transaction Output,UTXO)。你可以把它们想象成钱包里的一枚枚硬币:每枚都有固定面值、有来路记录,花的时候必须整枚花掉——面值不够就得多拿几枚凑,花不完的部分就要"找零"回来。
UTXO 由哪些字段组成?
在 token-core-android 中,UTXO 被建模为BitcoinTransaction.UTXO这个内部类,定义于app/src/main/java/org/consenlabs/tokencore/wallet/transaction/BitcoinTransaction.java:
| 字段 | 含义 |
|---|---|
txHash | 这枚硬币来自哪一笔交易(交易哈希) |
vout | 它是该交易的第几个输出(从 0 开始) |
amount | 硬币面值,单位是"聪"(1 BTC = 1 亿聪) |
address | 这枚硬币被锁定在哪个地址 |
scriptPubKey | 锁定脚本,证明这枚硬币"属于谁" |
derivedPath | 该地址在 HD 钱包中的派生路径(如0/22) |
sequence | 序列号,与 RBF 手续费替换等功能相关 |
所以一笔比特币交易签名的本质是:选若干 UTXO 作为输入 → 生成"收款输出 + 找零输出" → 对每个输入分别签名。
一次转账的完整旅程:输入、输出与找零怎么算?📐
用 token-core-android 发起转账非常简单:构造一个BitcoinTransaction,传入收款地址、找零索引、转账金额、手续费和 UTXO 列表,再调用signTransaction()即可。核心逻辑位于app/src/main/java/org/consenlabs/tokencore/wallet/transaction/BitcoinTransaction.java:
输入总和 = Σ 所有 UTXO 的 amount 找零金额 = 输入总和 - 转账金额 - 手续费这里有个容易忽略的细节:只有找零金额 ≥ 防粉尘阈值(DUST_THRESHOLD = 2730聪)时,才会追加找零输出。如果找零金额低于这个值,就直接放弃找零,把零头留给矿工作手续费。这样做是为了避免产生"粉尘输出"(dust),防止区块链被微小金额交易刷屏。
找零地址:钱包如何安全地收回零钱?🔒
找零地址绝对不能直接用收款地址——那样链上任何人都能顺着地址扒出你的全部资金流水,隐私瞬间归零。token-core-android 的做法是:从 HD 主密钥按 BIP44 规范派生出一个独立的找零分支(change key),再结合你传入的changeIdx定位到具体的找零地址。这段逻辑见app/src/main/java/org/consenlabs/tokencore/wallet/transaction/BitcoinTransaction.java。
常用的 BIP44 派生路径定义在app/src/main/java/org/consenlabs/tokencore/wallet/model/BIP44Util.java:
| 钱包类型 | 主网路径 | 测试网路径 |
|---|---|---|
| 普通比特币钱包 | m/44'/0'/0' | m/44'/1'/0' |
| SegWit 钱包 | m/49'/0'/0' | m/49'/1'/0' |
多输入签名实战:一枚硬币不够花怎么办?💰
当转账金额超过单枚 UTXO 的面值时,就需要把多个 UTXO 塞进同一笔交易。测试用例app/src/test/java/org/consenlabs/tokencore/wallet/transaction/BitcoinTransactionTest.java里就演示了同时消费 4 个 UTXO 的场景。
这里的关键在于:每个输入都必须用对应地址的私钥单独签名。token-core-android 会读取每个 UTXO 的derivedPath,从 HD 主密钥逐层派生出对应的子私钥,然后逐个计算签名哈希(hashForSignature)、生成 ECDSA 签名,最后写回每个输入的scriptSig。签名类型固定为SIGHASH_ALL,表示"签名覆盖整个交易,任何改动都会让签名失效"——这也是防篡改的核心。
私钥从哪来?WIF 钱包与 HD 钱包的差异
- WIF 钱包:从导入的 WIF 私钥直接恢复
ECKey,所有 UTXO 共用同一把私钥签名; - HD 钱包:从助记词派生出 xprv 主密钥,再按每个 UTXO 的
derivedPath分别派生对应子私钥。
SegWit 交易签名:见证数据与 wtxID 是什么?🧩
隔离见证(SegWit)改变了签名的组织方式:签名不再放进scriptSig,而是移入独立的见证数据区(witness),因此交易体积更小、手续费更省。token-core-android 通过signSegWitTransaction()实现 P2WPKH 地址的签名(app/src/main/java/org/consenlabs/tokencore/wallet/transaction/BitcoinTransaction.java),签名前需要预先计算hashPrevouts、hashOutputs、hashSequence等一系列哈希字段。
SegWit 交易还有一个独特现象:wtxID(见证交易 ID)和 txHash 可能不一样。wtxID 的哈希覆盖了见证数据,而 txHash 不含见证数据。这就是为什么签名结果里会同时返回两个哈希。
签名完成后拿到什么?📦
签名结果被封装在TxSignResult中(app/src/main/java/org/consenlabs/tokencore/wallet/transaction/TxSignResult.java):
| 返回值 | 用途 |
|---|---|
signedTx | 序列化后的完整签名交易(十六进制),可直接广播上链 |
txHash | 交易哈希,用于查询交易确认状态 |
wtxID | 仅 SegWit 交易返回,见证交易 ID |
新手避坑指南:5 个最容易踩的坑 ⚠️
- 单位换算:金额与手续费都用"聪",别拿 BTC 当单位直接传,差 1 亿倍;
- 防粉尘拦截:转账金额低于 2730 聪会直接被拒绝,这是防粉尘保护机制;
- 余额不足:输入 UTXO 总和小于转账金额时,会抛出
INSUFFICIENT_FUNDS异常; - 测试网隔离:调试务必使用测试网(TestNet)地址和测试网 WIF,别拿主网私钥乱试;
- 压缩公钥:SegWit 地址要求压缩公钥,导入非压缩私钥会直接报错。
如何获取源码动手实践?🚀
想要亲手跑一遍多输入签名与找零逻辑,可以克隆源码到本地:
git clone https://gitcode.com/gh_mirrors/to/token-core-android然后重点阅读BitcoinTransactionTest.java中的测试用例——它们演示了 WIF 钱包、HD 钱包、多 UTXO、SegWit 等多种场景,还附带了可校验的签名结果,是学习比特币交易签名的最佳入门材料。
【免费下载链接】token-core-androida blockchain private key management library on android项目地址: https://gitcode.com/gh_mirrors/to/token-core-android
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考