Raw-Transaction

你如何將你的 bitcoinjs 集成從 TransactionBuilder 切換到 transactions-psbt.js?

  • March 23, 2020

下面的程式碼有效:

const btc = require('bitcoinjs-lib');

var tx = new btc.TransactionBuilder(net);
inputs.forEach(o => tx.addInput(o.txId, o.vout));
outputs.forEach(o => tx.addOutput(o.address, o.value));
tx.sign(0, owner);
var body = tx.build().toHex();

但它抱怨 TransactionBuilder 已被棄用……嘆息……

棄用警告:TransactionBuilder 將來會被移除。(v6.xx 或更高版本)請改用 Psbt 類。我們 Github 上的 transactions-psbt.js 集成測試文件中提供了使用範例。psbt.ts 和 psbt.js 文件中也提供了高級解釋

顯然簽名方法也發生了變化(grr):

已棄用:TransactionBuilder 簽名方法參數將在 v6 中更改,請使用 TxbSignArg 介面

我已閱讀有關 Psbt 的文件並了解一般概念,但尚未找到有助於過渡的指南。我試圖簡單地改變tx.sign()tx.signInput()但它嘔吐

我還查看了該庫的測試套件,但它太複雜了,我無法理解。如何將我的程式碼轉換為新機制?

你只簽署第一個輸入?tx.sign(0, owner);通常每個輸入都必須以某種方式簽名。

我也為切換到 Psbt 而苦苦掙扎。最大的區別基本上是要求非隔離見證輸入的每個輸入都有一個完整的交易十六進制。隔離見證輸入可以更簡單,但也可以使用非隔離見證命令使用 tx 的完整十六進制創建,因此不必在輸入之間添加額外的邏輯區分。您不能再只提供 txid 和 vout,只需添加為您的對象創建每個輸入的 tx 的十六進制。

我真的只是按照範例<https://github.com/bitcoinjs/bitcoinjs-lib#examples>

所以這是我對我現在使用的帶有 psbt 的非常通用的 tx 的看法:

import * as bitcoin from 'bitcoinjs-lib'

// not all parts of inputs have to be defined
// inputs - array of all data needed for inputs
// outputs - array of all data needed for outputs

const network = bitcoin.networks.testnet // or bitcoin.networks.bitcoin

const psbt = new bitcoin.Psbt({ network })
psbt.setVersion(2)    // default
psbt.setLocktime(0)   // default

// ************** create inputs **************

inputs.forEach(input=&gt; {
 psbt.addInput({
   hash: input.txid,  // txid number
   index: input.vout,  // output number
   sequence: input.sequence, // often 0xfffffffe    
   nonWitnessUtxo: Buffer.from(input.hex, 'hex'), // works for witness inputs too!
   redeemScript: input.redeemScript, // only if there's redeem script
   witnessScript: input.witnessScript // only if there's witness script
 }) 
})

// ************** create outputs **************

outputs.forEach(output =&gt; {

 psbt.addOutput({
   address: output.address, // only if not op_return type output (no address for those)
   value: output.value,
   script: output.data  // only if there's string (utf8) data to embed in op_return
     ? bitcoin.payments.embed({ data: [Buffer.from(output.data, 'utf8')] }).output 
     : undefined
 }) 
})

// ************** signing **************

inputs.forEach((input, index) =&gt; {

 // sign regular inputs that can be simply signed
 if (!input.redeemScript && !input.witnessScript) {
   psbt.signInput(index, input.keyPair)  

   // give error if signature failed
   if (!psbt.validateSignaturesOfInput(index)) {
     throw new Error('Signature validation failed for input index ' + index.toString())
   }    
 }

})

// ************** finalizing inputs **************

inputs.forEach((input, index) =&gt; {

 // sign regular inputs that can be simply signed
 if (!input.redeemScript && !input.witnessScript) {
   psbt.finalizeInput(index) 
 }

 // for p2sh or p2wsh script inputs
 if (input.redeemScript || input.witnessScript) {
   psbt.finalizeInput(
     index, 
     getFinalScripts({ 
       inputScript: input.inputScript, 
       network 
     })
   )
 }
})

// ************** make tx **************

const tx = psbt.extractTransaction()

const virtualSize = tx.virtualSize()
const txid = tx.getId()
const hex = tx.toHex()

console.log('tx virtualSize:', virtualSize)
console.log('tx txid:', txid)
console.log('tx hex:', hex)

出於某種原因,自定義腳本的 finalizeInput 尚未完全內置,但我在範例中找到了它,只是將其添加為我導入或放在同一文件中某處的函式。

function getFinalScripts ({ inputScript, network }) {
 return function (
   inputIndex,
   input,
   script,
   isSegwit,
   isP2SH,
   isP2WSH,
 ) {
   // Step 1: Check to make sure the meaningful script matches what you expect.

   // Step 2: Create final scripts
   let payment = {
     network,
     output: script,
     input: inputScript,
   };
   if (isP2WSH && isSegwit)
     payment = bitcoin.payments.p2wsh({
       network,
       redeem: payment,
     });
   if (isP2SH)
     payment = bitcoin.payments.p2sh({
       network,
       redeem: payment,
     });

   function witnessStackToScriptWitness(witness) {
     let buffer = Buffer.allocUnsafe(0);

     function writeSlice(slice) {
       buffer = Buffer.concat([buffer, Buffer.from(slice)]);
     }

     function writeVarInt(i) {
       const currentLen = buffer.length;
       const varintLen = varuint.encodingLength(i);

       buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
       varuint.encode(i, buffer, currentLen);
     }

     function writeVarSlice(slice) {
       writeVarInt(slice.length);
       writeSlice(slice);
     }

     function writeVector(vector) {
       writeVarInt(vector.length);
       vector.forEach(writeVarSlice);
     }

     writeVector(witness);

     return buffer;
   }

   return {
     finalScriptSig: payment.input,
     finalScriptWitness:
       payment.witness && payment.witness.length &gt; 0
         ? witnessStackToScriptWitness(payment.witness)
         : undefined,
   };
 }
}

我還沒有測試過所有類型的輸入,但這基本上是我用於 p2wpkh 和 p2wsh 輸入的。我試圖找到完全符合我通常需要的輸入類型的範例。

首先讓我說我是一個完整的新手,所以我可能是錯的。關於 sign 方法的棄用,已經棄用的不是方法 tx.sign() 本身,而是我們傳遞參數的方式。為了不為 sign 方法獲取 DEPRECATED 消息,您應該傳遞表單的對象

{ prevOutScriptType:字元串;vin:數字;密鑰對:簽名者;贖回腳本?:緩衝區;hashType?:數字;見證價值?:數字;見證腳本?:緩衝區;}

(見<https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/transaction_builder.ts>第 74 行)

在你的情況下如此具體

tx.sign({prevOutScriptType:‘p2pkh’, vin: 0, keyPair: owner})

引用自:https://bitcoin.stackexchange.com/questions/92758