You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用HSM作为NBitcoin外部签名器时遇签名及手续费错误求助

解决HSM签名比特币交易的两个错误:SigHashType脚本错误与手续费过低

1. 解决SigHashType脚本错误

错误原因

比特币交易签名并非直接对整个交易数据哈希,而是需要针对每个输入生成签名哈希(Signature Hash),并在哈希末尾追加SIGHASH类型字节(默认是SigHash.All对应的0x01)。你的代码直接对整个交易Hex做双重SHA256哈希后签名,未处理SIGHASH类型,导致签名不被交易验证脚本认可;同时构造TransactionSignature时未明确指定SigHashType,也会引发匹配问题。

解决方案

步骤1:修改交易签名流程,生成正确的签名哈希

调用HSM签名前,先用NBitcoin的Transaction.ComputeSignatureHash方法生成对应输入的签名哈希(已包含双重SHA256和SIGHASH字节):

var network = Network.TestNet;
var destination = BitcoinAddress.Create(receiverAddress, network);
var sender = BitcoinAddress.Create(senderAddress, network);

var unspentCoins = await GetUnSpentCoins(senderAddress, network);
var builder = network.CreateTransactionBuilder();
var tx = builder
    .AddCoins(unspentCoins)
    .Send(destination, Money.Coins(0.00001M))
    .SetChange(sender.ScriptPubKey)
    .BuildTransaction(false);

// 生成输入0对应的签名哈希(带SigHash.All标识)
var sigHash = tx.ComputeSignatureHash(0, sender.ScriptPubKey, SigHash.All, tx.Inputs[0].ScriptSig);

var wallet = HSMWallet.Load(walletName);
// 把签名哈希传给HSM,而非整个交易Hex
var signedTx = wallet.SignTransaction(Encoders.Hex.EncodeData(sigHash));

// 构造TransactionSignature时明确指定SigHash.All
var pubKey = new NBitcoin.PubKey(wallet.PubKey.RawPubKey);
var signature = new TransactionSignature(Encoders.Hex.DecodeData(signedTx), SigHash.All);

// 添加已知签名后无需再调用SignTransactionInPlace
builder.AddKnownSignature(pubKey, signature, unspentCoins[0].Outpoint);

var error = builder.Check(tx);
var verify = builder.Verify(tx);

步骤2:修改HSM的SignTransaction方法

HSM只需对传入的签名哈希直接签名,无需重复做双重哈希:

public string SignTransaction(string sigHashHex)
{
    var mechanism = Session.Factories.MechanismFactory.Create(CKM.CKM_ECDSA);
    var sigHashBytes = Convert.FromHexString(sigHashHex);
    
    // 直接对签名哈希执行ECDSA签名
    var signature = Session.Sign(mechanism, PrivateKeyHandle, sigHashBytes);

    Session.Verify(mechanism, PublicKeyHandle, sigHashBytes, signature, out bool isValid);
    if (!isValid)
        throw new Exception("签名验证失败!");

    return ConvertUtils.BytesToHexString(ConstructEcdsaSigValue(signature));
}

2. 解决手续费过低问题

错误原因

代码同时使用SubtractFees()和SendEstimatedFees(new FeeRate(1m))导致逻辑冲突,且设置的1 sat/vbyte费率低于当前测试网的最低手续费要求。

解决方案

步骤1:简化手续费设置逻辑

保留一种手续费计算方式,提高费率至符合网络要求的值(比如2 sat/vbyte):

var tx = builder
    .AddCoins(unspentCoins)
    .Send(destination, Money.Coins(0.00001M))
    .SendEstimatedFees(new FeeRate(2m)) // 调整为2 sat/vbyte
    .SetChange(sender.ScriptPubKey)
    .BuildTransaction(false);

步骤2:手动计算精确手续费(可选)

若需要更精准控制,可手动估算交易大小并计算手续费:

var feeRate = new FeeRate(2m); // 2 sat/vbyte
var estimatedSize = builder.EstimateTransactionSize();
var requiredFee = feeRate.GetFee(estimatedSize);

var tx = builder
    .AddCoins(unspentCoins)
    .Send(destination, Money.Coins(0.00001M))
    .SendFees(requiredFee)
    .SetChange(sender.ScriptPubKey)
    .BuildTransaction(false);

内容的提问来源于stack exchange,提问作者Rahmat Anjirabi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 02:24:22