如何使用bitcoin-ruby广播原始比特币交易?(含报错排查)
解决比特币测试网交易广播的高手续费报错问题
嗨,我看到你用bitcoin-ruby gem在测试网广播交易时遇到了「手续费过高」的报错,这个问题其实很常见——核心原因是你没有添加找零输出,导致原UTXO的剩余金额全部被当成手续费,远远超出了节点允许的范围。下面我来一步步帮你修复:
问题根源拆解
比特币交易的规则是:所有输入的总金额 = 所有输出的总金额 + 手续费。你现在的代码只指定了一个目标输出,相当于把原交易输入的全部金额减去50000聪的部分都当成了手续费,这显然远远超过了正常手续费(测试网一般几千聪就够),所以节点直接拒绝了你的交易。
修复后的完整代码
我调整了代码逻辑,主要补全了UTXO金额获取、找零计算和交易广播的部分,你可以直接参考:
Bitcoin.network = :testnet # 配置你的地址、私钥和目标地址 your_address = "muLGqMS6tkqb9sBQ4sqeXjMzaV8rk8o3gv" your_private_key = Connect.get_private_key # 确保这个方法能正确返回你的私钥 dest_address = "msPHTrHSktDLMwPXcMYwWTqth3ZyykN17H" # 1. 获取你的地址下未花费的UTXO(直接拿到可用金额,避免手动计算错误) utxo_url = "https://testnet.blockexplorer.com/api/addr/#{your_address}/utxo" uri = URI(utxo_url) response = Net::HTTP.get(uri) utxos = Connect.valid_json(response) ? JSON.parse(response) : [] # 取第一个可用的UTXO(如果有多个,你可以选金额合适的) prev_utxo = utxos.first prev_tx_hash = prev_utxo["txid"] prev_output_index = prev_utxo["vout"] prev_output_value = prev_utxo["amount"] * 100000000 # 转换为聪(1BTC = 1亿聪) # 2. 获取前置交易的完整数据 raw_tx_url = "https://testnet.blockexplorer.com/api/rawtx/#{prev_tx_hash}" uri = URI(raw_tx_url) response = Net::HTTP.get(uri) raw_prev_tx = Connect.valid_json(response) ? JSON.parse(response)["rawtx"] : "" prev_tx = Bitcoin::P::Tx.new(raw_prev_tx.htb) # 3. 创建新交易 tx = Bitcoin::Protocol::Tx.new tx.add_in Bitcoin::Protocol::TxIn.new(prev_tx.binary_hash, prev_output_index, 0) # 4. 设置输出和找零 send_amount = 50000 # 要转给目标地址的金额(0.0005 BTC) min_fee = 1000 # 测试网最低手续费,可根据交易大小调整(一般1000-5000聪足够) change_amount = prev_output_value - send_amount - min_fee # 计算找零金额,转回自己的地址 # 添加目标地址输出 tx.add_out Bitcoin::Protocol::TxOut.value_to_address(send_amount, dest_address) # 添加找零输出(必须!否则剩余金额全变手续费) tx.add_out Bitcoin::Protocol::TxOut.value_to_address(change_amount, your_address) # 5. 签名交易输入 key = Bitcoin.open_key(your_private_key) sig = Bitcoin.sign_data(key, tx.signature_hash_for_input(0, prev_tx)) tx.in[0].script_sig = Bitcoin::Script.to_signature_pubkey_script(sig, [key.public_key_hex].pack("H*")) # 6. 验证签名并广播交易 if tx.verify_input_signature(0, prev_tx) puts "✅ 签名验证通过,准备广播交易..." # 使用blockexplorer的API广播原始交易 broadcast_url = "https://testnet.blockexplorer.com/api/tx/send" uri = URI(broadcast_url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri.path, {'Content-Type' => 'application/json'}) request.body = JSON.generate({rawtx: tx.to_payload.unpack("H*")[0]}) broadcast_response = http.request(request) puts "📢 广播响应: #{broadcast_response.body}" else puts "❌ 签名验证失败,请检查私钥和UTXO信息是否正确" end
关键调整点说明
- 正确获取UTXO金额:用
/addr/{address}/utxo接口直接拿到未花费输出的准确金额,避免了原代码中从交易列表拿数据后无法获取对应输出金额的问题。 - 强制添加找零输出:把扣除转账金额和手续费后的剩余金额转回自己的地址,确保手续费在合理范围内。
- 明确手续费设置:测试网不用设置高额手续费,1000聪基本就能快速确认。
- 完整的广播流程:直接在代码里实现了交易广播,不用手动复制hex去浏览器提交。
额外排查提示
如果运行后还是有问题,你可以检查这几点:
- 你的UTXO是否已经被花费?如果之前测试过,这个UTXO可能已经被用掉了,换一个UTXO试试。
- 私钥是否对应你的地址?签名错误会导致交易验证失败,节点也不会接收。
- 手续费是否设置合理?如果交易数据较大(比如多个输入输出),可以适当提高手续费到5000聪左右。
内容的提问来源于stack exchange,提问作者Ayan Nurbolat
相关产品推荐
相关产品推荐

