LedgerHQ / ledgerjs

⛔️ MOVED to monorepo "ledger-live"
https://github.com/LedgerHQ/ledger-live
Apache License 2.0
573 stars 381 forks source link

signP2SHTransaction produces an invalid signature #516

Open gregdhill opened 3 years ago

gregdhill commented 3 years ago

Firmware: 1.6.0 Bitcoin Test (BTC): Version 1.4.2

Packages:

Summary

Signing a SegWit (P2WPKH-in-P2SH) input produces an invalid signature when compared with the output from bitcoinjs-lib when using the node client.

Example

const transport = await Transport.create();
const app = new AppBtc(transport);

// LEDGERJS

const NETWORK = bitcoin.networks.testnet;
const wallet = await app.getWalletPublicKey("49'/1'/0'/0/0", {format: "p2sh"});
const { publicKey } = bitcoin.ECPair.fromPublicKey(Buffer.from(wallet.publicKey, 'hex'));
const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: publicKey, network: NETWORK });
const p2sh_p2wpkh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: NETWORK });
const redeemScript = p2sh_p2wpkh.redeem.output || Buffer.from("");
console.log(bitcoin.crypto.hash160(redeemScript).toString('hex'))

const txIndex = 0;
const utxoHex = "0200000000010152f6cfe8d012999f9f8d8b08b7fe6c6e9e85c7137b1c792903647d5be13cb24d0100000017160014baa6f1ee481fc5af4e9547d59838a257559fdd65feffffff02102700000000000017a914999b7898fde542f276eb48d6a955b03884fdb315877c05f5010000000017a9147914a96f1497538de201e6a92be02e6f2528354a870247304402203cbdb580ce799e02f5367ed9ff280a73d4a6a3f97ff32837d66a2eaa4ef94eac022007c95fa98f414f1649fc4557f1bc959742d989536f3ce4bfb9cc8bdaed0b3ce5012102b0cd03960f60e26ac094f847a6781a1ed95ad1e05e4fa07d5081e07ef39897ef7f2a1b00";

const inTx = app.splitTransaction(utxoHex, true, false);

const address = (await app.getWalletPublicKey("49'/1'/0'/0/1", {format: "p2sh"})).bitcoinAddress;
const script = bitcoin.payments.p2sh({ address: address, network: NETWORK });
const outputScriptHex = app.serializeTransactionOutputs({
    version: Buffer.from("01000000", 'hex'),
    inputs: [],
    outputs: [{
        amount: toBufferLE(BigInt(100), 8),
        script: script.output || Buffer.from(""),
    }]
}).toString('hex');

let result = await app.signP2SHTransaction({
    inputs: [[inTx, txIndex, redeemScript.toString('hex'), null]],
    associatedKeysets: [ "49'/1'/0'/0/0" ],
    outputScriptHex,
    sigHashType: 1,
    transactionVersion: 3,
});
console.log(Buffer.from(result[0], 'hex'));

// BITCOINJS-LIB

const psbt = new bitcoin.Psbt({network: NETWORK});
const utxo = bitcoin.Transaction.fromHex(utxoHex);

const version = 1;
psbt.addInput({
    hash: utxo.getId(),
    index: txIndex,
    witnessUtxo: {
        script: utxo.outs[txIndex].script,
        value: utxo.outs[txIndex].value,
    },
    redeemScript: redeemScript,
});
psbt.addOutput({
    address: address,
    value: 100
});
psbt.setVersion(version);

// same as above
const mneumonic = ["brisk", "middle", "dune", "visit", "style", "film", "clip", "process", "enact", "coffee", "blur", "juice", "height", "account", "potato", "such", "fire", "track", "normal", "width", "merry", "loud", "bless", "camera"]
const seed = await bip39.mnemonicToSeed(mneumonic.join(" "))

const bip32 = bitcoin.bip32.fromSeed(seed, NETWORK);
const keyPair = bip32.derivePath("49'/1'/0'/0/0");

psbt.signAllInputs(keyPair);
psbt.finalizeAllInputs();

const finalTx = psbt.extractTransaction();
console.log(finalTx.toHex());
landabaso commented 2 years ago

The redeemScript you have set as input in signP2SHTransaction is wrong. You have to use the redeemScript of the input, not the one for the output. See more details here: https://github.com/bitcoinjs/bitcoinjs-lib/issues/1517#issuecomment-1064367562