how to sign bitcoin psbt with ledger

0 votes

I'm trying to sign a PSBT transaction from bitcoinjs-lib following what I found here:

https://github.com/helperbit/helperbit-wallet/blob/master/app/components/dashboard.wallet/bitcoin.service/ledger.ts

I've checked that the compressed publicKey both from ledger, and the one from bitcoinjsLib returned the same value.

I could sign it with the bitcoinjs-lib ECPair, but when I try to sign it using a ledger, it is always invalid. Can someone help me point out where I made a mistake?

These variables are already mentioned in the code below, but for clarity purpose:

- mnemonics: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about 

- previousTx: 02000000000101869362410c61a69ab9390b2167d08219662196e869626e8b0350f1a8e4075efb0100000017160014ef3fdddccdb6b53e6dd1f5a97299a6ba2e1c11c3ffffffff0240420f000000000017a914f748afee815f78f97672be5a9840056d8ed77f4887df9de6050000000017a9142ff4aa6ffa987335c7bdba58ef4cbfecbe9e49938702473044022061a01bf0fbac4650a9b3d035b3d9282255a5c6040aa1d04fd9b6b52ed9f4d20a022064e8e2739ef532e6b2cb461321dd20f5a5d63cf34da3056c428475d42c9aff870121025fb5240daab4cee5fa097eef475f3f2e004f7be702c421b6607d8afea1affa9b00000000 

- paths: 
["0'/0/0"] 

- redeemScript: (non-multisig segwit) 
00144328adace54072cd069abf108f97cf80420b212b

This is my minimum reproducible code I've got.

/* tslint:disable */ 
// @ts-check 
require('regenerator-runtime'); 
const bip39 = require('bip39'); 
const { default: Transport } = require('@ledgerhq/hw-transport-node-hid'); 
const { default: AppBtc } = require('@ledgerhq/hw-app-btc'); 
const bitcoin = require('bitcoinjs-lib'); 
const mnemonics = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; 
const NETWORK = bitcoin.networks.regtest; 

/** 
* @param {string} pk 
* @returns {string} 
*/ 
function compressPublicKey(pk) { 
    const { publicKey } = bitcoin.ECPair.fromPublicKey(Buffer.from(pk, 'hex')); 
    return publicKey.toString('hex'); 
} 

/** @returns {Promise<any>} */ 
async function appBtc() { 
      const transport = await Transport.create(); 
      const btc = new AppBtc(transport); return btc; } 
      const signTransaction = async() => { 
      const ledger = await appBtc(); 
      const paths = ["0'/0/0"]; 
      const [ path ] = paths; 
      const previousTx = "02000000000101869362410c61a69ab9390b2167d08219662196e869626e8b0350f1a8e4075efb0100000017160014ef3fdddccdb6b53e6dd1f5a97299a6ba2e1c11c3ffffffff0240420f000000000017a914f748afee815f78f97672be5a9840056d8ed77f4887df9de6050000000017a9142ff4aa6ffa987335c7bdba58ef4cbfecbe9e49938702473044022061a01bf0fbac4650a9b3d035b3d9282255a5c6040aa1d04fd9b6b52ed9f4d20a022064e8e2739ef532e6b2cb461321dd20f5a5d63cf34da3056c428475d42c9aff870121025fb5240daab4cee5fa097eef475f3f2e004f7be702c421b6607d8afea1affa9b00000000" 
    const utxo = bitcoin.Transaction.fromHex(previousTx); 
    const segwit = utxo.hasWitnesses(); 
    const txIndex = 0; // ecpairs things. 
    const seed = await bip39.mnemonicToSeed(mnemonics); 
    const node = bitcoin.bip32.fromSeed(seed, NETWORK); 
    const ecPrivate = node.derivePath(path); 
    const ecPublic = bitcoin.ECPair.fromPublicKey(ecPrivate.publicKey, { network: NETWORK });   const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: ecPublic.publicKey, network: NETWORK }); const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: NETWORK }); 
const redeemScript = p2sh.redeem.output; 
const fromLedger = await ledger.getWalletPublicKey(path, { format: 'p2sh' }); 
const ledgerPublicKey = compressPublicKey(fromLedger.publicKey); 
const bitcoinJsPublicKey = ecPublic.publicKey.toString('hex'); 

console.log({ ledgerPublicKey, bitcoinJsPublicKey, address: p2sh.address, segwit, fromLedger, redeemScript: redeemScript.toString('hex') }); 
var tx1 = ledger.splitTransaction(previousTx, true); 
const psbt = new bitcoin.Psbt({ network: NETWORK }); 
psbt.addInput({ 
    hash: utxo.getId(), 
    index: txIndex, 
    nonWitnessUtxo: Buffer.from(previousTx, 'hex'), 
    redeemScript, 
}); 
psbt.addOutput({ 
      address: 'mgWUuj1J1N882jmqFxtDepEC73Rr22E9GU', 
      value: 5000, 
}); 
psbt.setMaximumFeeRate(1000 * 1000 * 1000); // ignore maxFeeRate we're testnet anyway. psbt.setVersion(2); 
/** @type {string} */ 
// @ts-ignore 
const newTx = psbt.__CACHE.__TX.toHex(); 
console.log({ newTx }); 

const splitNewTx = await ledger.splitTransaction(newTx, true); 
const outputScriptHex = await ledger.serializeTransactionOutputs(splitNewTx).toString("hex"); const expectedOutscriptHex = '0188130000000000001976a9140ae1441568d0d293764a347b191025c51556cecd88ac'; // stolen from: https://github.com/LedgerHQ/ledgerjs/blob/master/packages/hw-app-btc/tests/Btc.test.js console.log({ outputScriptHex, expectedOutscriptHex, eq: expectedOutscriptHex === outputScriptHex }); const inputs = [ [tx1, 0, p2sh.redeem.output.toString('hex') /** ??? */] ]; const ledgerSignatures = await ledger.signP2SHTransaction( inputs, paths, outputScriptHex, 0, // lockTime, undefined, // sigHashType = SIGHASH_ALL ??? utxo.hasWitnesses(), 2, // version??, ); const signer = { network: NETWORK, publicKey: ecPrivate.publicKey, /** @param {Buffer} $hash */ sign: ($hash) => { const expectedSignature = ecPrivate.sign($hash); // just for comparison. const [ ledgerSignature0 ] = ledgerSignatures; const decodedLedgerSignature = bitcoin.script.signature.decode(Buffer.from(ledgerSignature0, 'hex')); console.log({ $hash: $hash.toString('hex'), expectedSignature: expectedSignature.toString('hex'), actualSignature: decodedLedgerSignature.signature.toString('hex'), }); // return signature; return decodedLedgerSignature.signature; }, }; psbt.signInput(0, signer); const validated = psbt.validateSignaturesOfInput(0); psbt.finalizeAllInputs(); const hex = psbt.extractTransaction().toHex(); console.log({ validated, hex }); }; if (process.argv[1] === __filename) { signTransaction().catch(console.error) 
Mar 9 in Blockchain by Soham
• 8,730 points
124 views

No answer to this question. Be the first to respond.

Your answer

Your name to display (optional):
Privacy: Your email address will only be used for sending these notifications.

Related Questions In Blockchain

0 votes
1 answer

How to open bitcoin-qt wallet with bitcoinj?

I found this issue solved at the ...READ MORE

answered Aug 24, 2018 in Blockchain by slayer
• 29,310 points
311 views
0 votes
1 answer

How to get price from bitcoin to USD with api

If I'm not wrong, is this what ...READ MORE

answered Apr 7 in Blockchain by Aditya
• 7,280 points
40 views
0 votes
1 answer
0 votes
1 answer

How can I simulate private bitcoin network with random peer discovery?

You can model the whatever network you ...READ MORE

answered May 29, 2018 in Blockchain by Johnathon
• 9,090 points
220 views
0 votes
1 answer

How i can use nodejs to watch transactions in bitcoin network?

you can use  const Socket = require('blockchain.info/Socket'); const mySocket ...READ MORE

answered Jul 9, 2018 in Blockchain by digger
• 26,720 points
525 views
0 votes
1 answer

How to check Bitcoin Wallet ?

This can be done using Extended Public ...READ MORE

answered Jul 13, 2018 in Blockchain by Perry
• 17,100 points
574 views
0 votes
1 answer

Protocols used in a distributed/dlt system for the nodes to establish communication

yes all are over TCP/IP connections secured by TLS encryption in hashgraph architecture-hashgraph, ...READ MORE

answered Aug 6, 2018 in Blockchain by aryya
• 7,440 points
609 views
0 votes
1 answer

Truffle tests not running after truffle init

This was a bug. They've fixed it. ...READ MORE

answered Sep 11, 2018 in Blockchain by Christine
• 15,790 points
925 views
0 votes
1 answer
webinar REGISTER FOR FREE WEBINAR X
Send OTP
REGISTER NOW
webinar_success Thank you for registering Join Edureka Meetup community for 100+ Free Webinars each month JOIN MEETUP GROUP