当前位置: 首页 > 工具软件 > BitcoinJS > 使用案例 >

【bitcoinjs-lib】签名错误提示:Error: No inputs were signed

滑令
2023-12-01

基于【bitcoinjs-lib】的转出钱包地址【是否隔离见证,生成方式】都会决定是否签名成功!

import bitcoin from "bitcoinjs-lib";
 
/**
 * 实战环境温馨提示:
 *      1、基于【bitcoinjs-lib】转账交易封装比较复杂,更适合了解Bitcoin交易的原理【自己封装可能存在各种小Bug】
 *      2、基于【bitcore-lib】转账交易是bitpay官方封装,简单易用,更适合生产环境!
 */
 
export default class BitcoinJS extend BitcoinBase {
 
    async sendBitcoinNetworkTransactionCommon(token_type, utxo_inputs, to, fee_satoshis, to_btc_satoshis_amount, amount, total_btc_satoshis_balance) {
        /**
         * 功能:重写父类方法【基于原生的bitcoinjs-lib的转账方法】
         * 基于【bitcoinjs-lib】对比【bitcore-lib】:都是基于离线签名【绝对安全】都是基于【MIT】发布的证书
         *      a、基于【bitcoinjs-lib】
         *          1、封装比较复杂,基于各种Address的特性,input需要携带不一样的参数!
         *          2、转账Omni-USDT未完成确认之前,转账全部BTC余额是无法广播成功【只能转账非参与USDT的BTC部分】
         *      b、基于【bitcore-lib】一切问题都是自动解决了,因为bitpay官方内部已做好一切封装,直接调用即可!
         */
        const psbt = new bitcoin.Psbt({ network: this.originalBitcoinNetworkNode });
        for (const _utxo_input of utxo_inputs) {
            /**
             * 1、基于【bitcoinjs-lib】的执行转出的wallet地址,必须根据地址的特性配置一些特殊参数!
             * 2、请参考官方案例:https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts
             */
            psbt.addInput({
                hash: _utxo_input.txId,
                index: _utxo_input.outputIndex,
                ...await this.__getAddressInputNonOrWitnessUtxoObject(_utxo_input),
                ...this.__getAddressRedeemOrWitnessScriptObject(),
            });
        }
        if (token_type === "usdt") {
            /**
             * 1、这里的【_transaction.addData(string)会自动生成【OP_RETURN】格式的数据【等价于new bitcore.Script.buildDataOut(string)】
             * 2、参考官方文档:https://github.com/bitpay/bitcore-lib/blob/master/docs/examples.md#create-an-op-return-transaction
             */
            psbt.addOutput({
                script: Buffer.from(this.generateOmniUSDTOutputOpReturnScript(amount)), value: 0,
            });
        }
        psbt.addOutput({
            address: to, value: to_btc_satoshis_amount,
        });
        let _remainingBTCBalanceSatoshis = total_btc_satoshis_balance - to_btc_satoshis_amount - fee_satoshis;
        if (_remainingBTCBalanceSatoshis > 0) {
            /**
             * 1、【非常重要】每次交易当前Wallet剩余的余额必须再次转给自己【change找零地址】否则剩余余额部分会全部自动变成手续费fee消耗了【直接挂逼!!!】
             * 2、比特币交易原理:
             *      a、取出全部可用【UtxoInput】余额,然后一次性分配给【toAddress金额】+【剩余的数量转入找零change地址】+【fee手续费】
             *      b、change找零地址,通常为当前转出钱包地址
             *      c、【务必注意】如果【未提供change找零地址】则全部可用【UtxoInput】余额自动分配给【toAddress金额】+【剩余的数量全部自动当fee手续费消耗了】
             */
            psbt.addOutput({
                address: this.walletAddress, value: _remainingBTCBalanceSatoshis,
            });
        }
        // 签名全部inputs
        psbt.signAllInputs(this.keyPair);
        // 序列化全部inputs
        psbt.finalizeAllInputs();
        return this.broadcastTx(psbt.extractTransaction().toHex(), token_type, to, to_btc_satoshis_amount, amount);
    }
 
    async __getAddressInputNonOrWitnessUtxoObject(utxo_input) {
        /**
         * 功能:获取是否隔离见证地址的utxo特殊参数对象
         * 特殊说明:
         *      1、基于是否隔离见证地址,input传入的Utxo参数不一样!
         *      2、如果未提供对应的nonWitnessUtxo或witnessUtxo参数,签名会抛异常【Error: No inputs were signed】
         */
        if (this.addressType === "prefix_1") {
            /**
             * 1、转出Address如为非隔离见证【1开头地址】则必填参数nonWitnessUtxo【非隔离见证输入现在需要将整个前一个tx作为缓冲区传递】
             */
            return this.__getAddressNonWitnessUtxoObj(utxo_input);
        }
        return {
            /**
             * 1、转出Address如为隔离见证【bc1或3开头地址】则必填参数witnessUtxo【只需要Utxo的scriptPubkey和value】
             */
            witnessUtxo: {
                script: Buffer.from(utxo_input.script, "hex"), value: utxo_input.satoshis,
            },
        };
    }
 
    async __getAddressNonWitnessUtxoObj(utxo_input) {
        /**
         * 功能:获取已完成的utxo_input【等价于上一个previous】交易的tx_hex的Buffer对象【non-segwit inputs now require passing the whole previous tx as Buffer】
         * 特殊说明:
         *      1、注:每一个utxo_input相比【当前即将执行的转账交易来说】都是【previous】
         */
        return {
            nonWitnessUtxo: Buffer.from(
                (await this.getTx(utxo_input.txId)).tx_hex, "hex"),
        };
    }
 
    __getAddressRedeemOrWitnessScriptObject() {
        /**
         * 功能:获取address匹配的redeemScript或witnessScript对象!
         */
        let _addressObj = this.getWalletAddress(true);
        /**
         * 1、address基于P2SH方式生成,则必须redeemScript
         * 2、address基于P2WSH方式生成,则必须witnessScript
         * 3、address基于其他方式生成,则以上2个参数都不需要!
         */
        if (_addressObj.redeem) {
            return {
                redeemScript: _addressObj.redeem.output,
            };
        }
        if (_addressObj.witness) {
            return {
                witnessScript: _addressObj.witness.output,
            };
        }
        return {};
    }
}

参考:

【重磅推荐】基于【bitcoinjs-lib】对比【bitcore-lib】两个库开发Bitcoin-Wallet的实战总结_比特币爱好者007的博客-CSDN博客

 类似资料: