Skip to content
This repository has been archived by the owner on Jun 27, 2022. It is now read-only.

how can I apply signature generated from signP2SHtransaction to transaction? #248

Closed
qiluge opened this issue Nov 22, 2018 · 9 comments
Closed

Comments

@qiluge
Copy link

qiluge commented Nov 22, 2018

the following code is used to construct 2/2 multi-sign transaction enable to broadcast to bitcoin test net, but I the signature generated from ledger is always verify failed. How can I generated correct signature for P2SH transaction from ledger nano s?

import Transport from "@ledgerhq/hw-transport-node-hid";
// import Transport from "@ledgerhq/hw-transport-u2f"; // for browser
import AppBtc from "@ledgerhq/hw-app-btc";

const bitcore = require("bitcore-lib");
const Script = bitcore.Script;

// cbd6884673d158f5b4856ac112a7fb9b78b0dc673bcf103bff8e35347fdc9ad8 2NFXzBKK3e6P4UCz8hRoKzJpZFpm1wNavGJ
async function multiSign() {
    let txObj = constructTx();
    let rawTx = txObj.toString("hex");
    console.log('raw tx generated by bitcore-lib', rawTx);
    const transport = await Transport.create();
    const btc = new AppBtc(transport);
    let pubKey1 = await btc.getWalletPublicKey("44'/1'/0'/0/0");
    console.log(pubKey1);
    let pubKey2 = await btc.getWalletPublicKey("44'/1'/0'/0/1");
    console.log(pubKey2);
    let bitCorePublicKey1 = bitcore.PublicKey(getCompressPublicKey(pubKey1.publicKey), {
        network: bitcore.Networks.testnet,
        compressed: true
    });
    let bitCorePublicKey2 = bitcore.PublicKey(getCompressPublicKey(pubKey2.publicKey), {
        network: bitcore.Networks.testnet,
        compressed: true
    });
    console.log("bit core test public key 1", bitCorePublicKey1.toString());
    console.log("bit core test public key 2", bitCorePublicKey2.toString());
    let publicKeys = [bitCorePublicKey1, bitCorePublicKey2];
    let multiSignAddr = new bitcore.Address(publicKeys, 2, bitcore.Networks.testnet);
    console.log("bit core multi sign addr is:", multiSignAddr.toString());
    let redeemScript = Script.buildMultisigOut(publicKeys, 2);
    let redeemScriptHash = redeemScript.toScriptHashOut();
    let utxo1 = "0200000001cf9a053c5097b9acc7ae52f19503933bb424401cde73592614caa8a1c253c25e00000000171600144ff787" +
        "a2829a087ef5ee6e195347f677a89f0d2dfeffffff027b8767c76f00000017a9140be2443d70212316cb0e1c3706bb3a989ec84a6" +
        "b877bdece000000000017a914f47e4f8e6da789aa3197529b3eb08b5f701b73ca87ca0a1600";
    let ledgerTx = btc.splitTransaction(utxo1);
    let signature1 = await btc.signP2SHTransaction(
        [[ledgerTx, 0, redeemScriptHash.toHex()]],
        ["44'/1'/0'/0/0"],
        btc.serializeTransactionOutputs(btc.splitTransaction(rawTx)).toString('hex')
    );
    let signature2 = await btc.signP2SHTransaction(
        [[ledgerTx, 0, redeemScriptHash.toHex()]],
        ["44'/1'/0'/0/1"],
        btc.serializeTransactionOutputs(btc.splitTransaction(rawTx)).toString('hex')
    );
    let s1 = {
        inputIndex: 1,
        signature: bitcore.crypto.Signature.fromString(signature1[0]),
        sigtype: 1,
        publicKey: bitCorePublicKey1
    };
    txObj.applySignature(s1);
    let s2 = {
        inputIndex: 1,
        signature: bitcore.crypto.Signature.fromString(signature2[0]),
        sigtype: 1,
        publicKey: bitCorePublicKey2
    };
    txObj.applySignature(s2);
    console.log('result is ', txObj.toString('hex'))
}

function constructTx() {
    let selfinputs = [
        {
            txid: 'cbd6884673d158f5b4856ac112a7fb9b78b0dc673bcf103bff8e35347fdc9ad8',
            vout: 1,
            address: '2NFXzBKK3e6P4UCz8hRoKzJpZFpm1wNavGJ',
            scriptPubKey: 'a914f47e4f8e6da789aa3197529b3eb08b5f701b73ca87',
            satoshis: 13557371,
            publicKeys: [
                '03363db333abea7bda3f31c69aea0cf3ecd5bc213127ddfcfc375f850c0e3d6343',
                '02475620337fc50083f6321369483c2abfcb066855d4e9988d51212109ae45822f'
            ]
        }];

    let selfoutputs = [{
        amount: 3557371,
        toAddress: 'mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB'
    }];

    let txObj = bitcore.Transaction();

    selfinputs.forEach(function (i) {
        txObj.from(i, i.publicKeys, 2);
    });

    selfoutputs.forEach(function (o) {
        txObj.to(o.toAddress, o.amount);
    });

    txObj.change('2NFXzBKK3e6P4UCz8hRoKzJpZFpm1wNavGJ');

    return txObj;
}

function getCompressPublicKey(publicKey) {
    let compressedKeyIndex;
    if (parseInt(publicKey.substring(128, 130), 16) % 2 !== 0) {
        compressedKeyIndex = "03";
    } else {
        compressedKeyIndex = "02";
    }
    const result = compressedKeyIndex + publicKey.substring(2, 66);
    return result;
}

multiSign();

the result and error is

raw tx generated by bitcore-lib 0100000001d89adc7f34358eff3b10cf3b67dcb0789bfba712c16a85b4f558d1734688d6cb01000000490047522102475620337fc50083f6321369483c2abfcb066855d4e9988d51212109ae45822f2103363db333abea7bda3f31c69aea0cf3ecd5bc213127ddfcfc375f850c0e3d634352aeffffffff02fb473600000000001976a9149f9a7abd600c0caa03983a77c8c3df8e062cb2fa88ace00f97000000000017a914f47e4f8e6da789aa3197529b3eb08b5f701b73ca8700000000
{ publicKey:
   '04363db333abea7bda3f31c69aea0cf3ecd5bc213127ddfcfc375f850c0e3d634365ffdcfafcf9201f17e2f2bd9853ce1acd5d9508fd8bfe4e989da7583297348d',
  bitcoinAddress: '1BZPFYaEC62UwyN3EcCYeJ8x5BYqLq4x91',
  chainCode:
   '2cf3b9cc4f7b8095fc9ca650d938fcb5357adafb525936e9e3bde4f4beb8e5b1' }
{ publicKey:
   '04475620337fc50083f6321369483c2abfcb066855d4e9988d51212109ae45822f29f5ce9db37a19610bf5b72d4985abc6e06441dc2be5aa04a8f791401ff92410',
  bitcoinAddress: '15d8irvsQHFMyoHYS6Yd2yGMoVVPwzuwjU',
  chainCode:
   '914cf986b725cecfd230b4d97d872189c157d5900c84d9c1e16727f70f9c0112' }
bit core test public key 1 03363db333abea7bda3f31c69aea0cf3ecd5bc213127ddfcfc375f850c0e3d6343
bit core test public key 2 02475620337fc50083f6321369483c2abfcb066855d4e9988d51212109ae45822f
bit core multi sign addr is: 2NFXzBKK3e6P4UCz8hRoKzJpZFpm1wNavGJ
(node:22756) UnhandledPromiseRejectionWarning: Invalid state: undefined
Error
    at new NodeError (G:\js workdspace\Ledger test\node_modules\bitcore-lib\lib\errors\index.js:20:41)
    at Object.checkState (G:\js workdspace\Ledger test\node_modules\bitcore-lib\lib\util\preconditions.js:9:13)
    at MultiSigScriptHashInput.addSignature (G:\js workdspace\Ledger test\node_modules\bitcore-lib\lib\transaction\input\multisigscripthash.js:90:5)
    at Transaction.applySignature (G:\js workdspace\Ledger test\node_modules\bitcore-lib\lib\transaction\transaction.js:1092:37)
    at _callee$ (G:/js workdspace/Ledger test/test.js:54:11)
    at tryCatch (G:\js workdspace\Ledger test\node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:65:40)
    at Generator.invoke [as _invoke] (G:\js workdspace\Ledger test\node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:303:22)
    at Generator.prototype.(anonymous function) [as next] (G:\js workdspace\Ledger test\node_modules\babel-polyfill\node_modules\regenerator-runtime\runtime.js:117:21)
    at step (G:\js workdspace\Ledger test\test.js:105:191)
    at G:\js workdspace\Ledger test\test.js:105:361

the error is produced by
image

@qiluge
Copy link
Author

qiluge commented Nov 22, 2018

let s1 = {
        inputIndex: 0,
        outputIndex: 1,
        prevTxId: "328bb1de94ca74ee020d3986175a84e844e2795643eb148fe79c69e676f83df6",
        signature: bitcore.crypto.Signature.fromString(signature1[0]),
        sigtype: bitcore.crypto.Signature.SIGHASH_ALL,
        publicKey: bitCorePublicKey1
    };
    txObj.applySignature(s1);
    let s2 = {
        inputIndex: 0,
        outputIndex: 1,
        prevTxId: "328bb1de94ca74ee020d3986175a84e844e2795643eb148fe79c69e676f83df6",
        signature: bitcore.crypto.Signature.fromString(signature2[0]),
        sigtype: bitcore.crypto.Signature.SIGHASH_ALL,
        publicKey: bitCorePublicKey2
    };
    // txObj.inputs[0].sinatures = [s1, s2];
    // txObj.inputs[0]._updateScript();
    txObj.applySignature(s2);

these code is also failed.

@blockchainguyy
Copy link

Hi @qiluge ,
Before you apply the signature by txObj.applySignature(s2); to to transaction, just verify first is the signatures are valid against the transaction by using txObj.isValidSignature(s1); . It will show you that the signatures are valid or invalid.

@qiluge
Copy link
Author

qiluge commented Nov 22, 2018

thanks @ayush-techracers , I have try to valid two signatures, and there are both invalid

if (txObj.isValidSignature(s1)) {
        txObj.applySignature(s1);
    } else {
        console.log('s1 is invalid');
    }

    if (txObj.isValidSignature(s2)) {
        txObj.applySignature(s2);
    } else {
        console.log('s2 is invalid');
    }

@blockchainguyy
Copy link

blockchainguyy commented Nov 22, 2018

@qiluge this is the utxo that you are trying to consume is and output index of this utxo is vout: 1

 {
            txid: 'cbd6884673d158f5b4856ac112a7fb9b78b0dc673bcf103bff8e35347fdc9ad8',
            vout: 1,
            address: '2NFXzBKK3e6P4UCz8hRoKzJpZFpm1wNavGJ',
            scriptPubKey: 'a914f47e4f8e6da789aa3197529b3eb08b5f701b73ca87',
            satoshis: 13557371,
            publicKeys: [
                '03363db333abea7bda3f31c69aea0cf3ecd5bc213127ddfcfc375f850c0e3d6343',
                '02475620337fc50083f6321369483c2abfcb066855d4e9988d51212109ae45822f'
            ]
        }

So while signing the transaction from ledger device you are using vout 0 while it must be 1

 let signature1 = await btc.signP2SHTransaction(
        [[ledgerTx, 1, redeemScriptHash.toHex()]],
        ["44'/1'/0'/0/0"],
        btc.serializeTransactionOutputs(btc.splitTransaction(rawTx)).toString('hex')
    );

@qiluge
Copy link
Author

qiluge commented Nov 22, 2018

I'm so sorry,

let signature1 = await btc.signP2SHTransaction(
        [[ledgerTx, 1, redeemScriptHash.toHex()]],
        ["44'/1'/0'/0/0"],
        btc.serializeTransactionOutputs(btc.splitTransaction(rawTx)).toString('hex')
    );
    let signature2 = await btc.signP2SHTransaction(
        [[ledgerTx, 1, redeemScriptHash.toHex()]],
        ["44'/1'/0'/0/1"],
        btc.serializeTransactionOutputs(btc.splitTransaction(rawTx)).toString('hex')
    );
let s1 = {
        inputIndex: 0,
        outputIndex: 1,
        prevTxId: "328bb1de94ca74ee020d3986175a84e844e2795643eb148fe79c69e676f83df6",
        signature: bitcore.crypto.Signature.fromString(signature1[0]),
        sigtype: bitcore.crypto.Signature.SIGHASH_ALL,
        publicKey: bitCorePublicKey1
    };
    let s2 = {
        inputIndex: 0,
        outputIndex: 1,
        prevTxId: "328bb1de94ca74ee020d3986175a84e844e2795643eb148fe79c69e676f83df6",
        signature: bitcore.crypto.Signature.fromString(signature2[0]),
        sigtype: bitcore.crypto.Signature.SIGHASH_ALL,
        publicKey: bitCorePublicKey2
    };
    if (txObj.isValidSignature(s1)) {
        txObj.applySignature(s1);
    } else {
        console.log('s1 is invalid');
    }

    if (txObj.isValidSignature(s2)) {
        txObj.applySignature(s2);
    } else {
        console.log('s2 is invalid');
    }

these code also produced invalid signature...

@qiluge
Copy link
Author

qiluge commented Nov 22, 2018

@ayush-techracers and I use these code

txObj.inputs[0].signatures = [s1, s2];
    txObj.inputs[0]._updateScript();
    console.log('force result is ', txObj.toString('hex'));

to generated tx to broadcast, the blockcyper return

Error validating transaction: Error running script for input 0 referencing 090983eeebd0ff1df243e58d574f516d0d48a1e46f284d5f576e704153632235 at 1: Script was NOT verified successfully..

@qiluge
Copy link
Author

qiluge commented Nov 22, 2018

I have found problem:

let signature1 = await btc.signP2SHTransaction(
        [[ledgerTx, 1, redeemScript.toHex()]],
        ["44'/0'/0'/0/0"],
        btc.serializeTransactionOutputs(btc.splitTransaction(rawTx)).toString('hex')
    );

while call signP2SH, the redeemScript argument should use redeem script, not redeem script hash.

@qiluge
Copy link
Author

qiluge commented Nov 22, 2018

@ayush-techracers thank you very much, thanks for your help for so many days.

@qiluge qiluge closed this as completed Nov 22, 2018
@blockchainguyy
Copy link

blockchainguyy commented Nov 22, 2018

Hi @qiluge ,But have you tried with multiple utxos? Since my problem is when I am trying to sign the transaction with multiple utxos. Then I am getting first signature correct and rest of the signatures are invalid from ledger signP2SHTransaction method.
With one utxo it works fine for me as well. But you would have issue when will try to sign the transaction with multiple utxos.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants