Ich versuche, eine Psbt-Transaktion von bitcoinjs-lib gemäß dem, was ich hier gefunden habe, zu signieren:
Ich habe überprüft, ob der komprimierte publicKey sowohl aus dem Ledger als auch aus bitcoinjsLib denselben Wert zurückgegeben hat.
Ich könnte es mit dem bitcoinjs-lib ECPair signieren, aber wenn ich versuche, es mit dem Hauptbuch zu signieren, ist es immer ungültig.
Kann mir jemand helfen, darauf hinzuweisen, wo ich einen Fehler gemacht habe?
Diese Variablen werden bereits im folgenden Code erwähnt, jedoch aus Gründen der Übersichtlichkeit:
- mnemonics:
abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
- previousTx:
02000000000101869362410c61a69ab9390b2167d08219662196e869626e8b0350f1a8e4075efb0100000017160014ef3fdddccdb6b53e6dd1f5a97299a6ba2e1c11c3ffffffff0240420f000000000017a914f748afee815f78f97672be5a9840056d8ed77f4887df9de6050000000017a9142ff4aa6ffa987335c7bdba58ef4cbfecbe9e49938702473044022061a01bf0fbac4650a9b3d035b3d9282255a5c6040aa1d04fd9b6b52ed9f4d20a022064e8e2739ef532e6b2cb461321dd20f5a5d63cf34da3056c428475d42c9aff870121025fb5240daab4cee5fa097eef475f3f2e004f7be702c421b6607d8afea1affa9b00000000
- paths:
["0'/0/0"]
- redeemScript: (non-multisig segwit)
00144328adace54072cd069abf108f97cf80420b212b
Dies ist mein reproduzierbarer Mindestcode, den ich habe.
/* 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)
}
Buffer.from
, aber der obige Code ist nur, dass ich den Code aus der oben erwähnten Referenz kopypastiere, da es so aussieht, als ob der ursprüngliche Code im Browser ausgeführt werden soll.