Ledger Nono S (ledgerjs) signP2SHTransaction erzeugt eine ungültige Signatur

Ich habe Fehler bei der Verwendung von Signaturen erhalten, die von der signP2SHTransactionFunktion von erstellt wurden @ledgerhq/hw-app-btc. Also habe ich ein Skript mit bitcore-lib erstellt, um es mit der von Ledger erstellten Signatur zu vergleichen und die Änderung zu lokalisieren, die den Fehler verursacht. Ich habe auch alle meine Eingabeparameter überprüft und für richtig befunden.

Ich werde beide Skripte hier detailliert beschreiben, obwohl ich denke, dass es ein Problem der signP2SHTransaction-Funktion von Ledger ist.

Ich verwende die folgenden Abhängigkeiten und Knoten v8.9.3:

"@ledgerhq/hw-app-btc": "^4.17.0",
"@ledgerhq/hw-app-eth": "^4.19.0",
"@ledgerhq/hw-transport-node-hid": "^4.18.0",
"babel-runtime": "^6.26.0",
"bip32": "^0.1.0",
"bitcoinjs-lib": "^3.3.2",
"bitcore-lib": "^0.15.0"

Ich habe eine 2 von 2 Multisig-Adresse erstellt, indem ich die folgenden 2 Pfade meines Ledgers verwendet habe 48'/0'/0'/69/0/0: 48'/0'/0'/96/0/0, . Ich habe das folgende rawTx verwendet:

01000000016f4fbe65fe5fcb98028d67172f72bdeadc1f45cb49c50f2eb7aca4668e94d50a01000000490047522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752aeffffffff0220d613000000000017a9148eaba4fd80f515c78ddbc2509538e37c40ffcf1287904a96070000000017a914c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a98700000000

Ich habe das folgende Redeem-Skript verwendet:

522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752ae

Hier ist der Code, den ich verwendet habe, um es zu produzieren:

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

var publicKey1 = new PublicKey(
  "02a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c89312"
); // public key for path- 48'/0'/0'/69/0/0

var publicKey = new PublicKey(
  "02faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e7"
); // public key for path- 48'/0'/0'/96/0/0

var pubkeys = [publicKey, publicKey1];

var redeemScript = Script.buildMultisigOut(pubkeys, 2);

console.log(redeemScript.toHex());

Sie können es hier entschlüsseln: https://live.blockcypher.com/btc/decodetx/ Hier ist der Code meines Ledgers, der eine Signatur für 48'/0'/0'/69/0/0den Pfad erstellt:

const TransportHid = require("@ledgerhq/hw-transport-node-hid").default;
const AppBtc = require("@ledgerhq/hw-app-btc").default;

TransportHid.create()
  .then(async transport => {
    if (!transport) return console.log("err: Unable to establish connection");
    var btc = new AppBtc(transport);

    const rawTx = "01000000016f4fbe65fe5fcb98028d67172f72bdeadc1f45cb49c50f2eb7aca4668e94d50a01000000490047522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752aeffffffff0220d613000000000017a9148eaba4fd80f515c78ddbc2509538e37c40ffcf1287904a96070000000017a914c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a98700000000";
    const redeemHex =
      "522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752ae";
    const bufferedData = await btc.splitTransaction(rawTx);

    let input = [];

    input.push(bufferedData);
    input.push(1);
    input.push(redeemHex);
    const outputScript = btc
      .serializeTransactionOutputs(bufferedData)
      .toString("hex");

    console.log("\nOutput script hex:", outputScript);

    const accountIndex = 69;

    console.log("\npath:", `48'/0'/0'/${accountIndex}/0/0`);

    await btc
      .signP2SHTransaction(
        [input],
        [`48'/0'/0'/${accountIndex}/0/0`],
        outputScript
      )
      .then(sig => console.log("\n\nSig hash:", sig))
      .catch(err => console.log("\n\nErr:", err));
  })
  .catch(err => console.log(err));

Es gibt die folgende Signatur zurück, wenn ich das RedeemScript mit dem tx übergebe:

Sig hash: [ '304402203e24b5ad68c1fe3bf55a11afb6a61e3525c6f0a2780a0ee4bf37401bfd1445ff02207b18b7277b4f2625d04d0fcfd796300a9f6923c1df11a96876ab094138ea2a94' ]

Und ich finde es seltsam, dass ich beim Entfernen des Skriptparameters zum Einlösen der signP2SHTransactionFunktion (der als optional gekennzeichnet ist) eine andere Signatur erhalte:

Sig hash: [ '3045022100f40f8fa2b75196a50b2f2543a06a3ed3ed79814cd29510f1225a359620b1c19102201af093addc93fce4fe8c59ad127c42064c2840ec7c71fbf5d0bae7ee5cb4cd39' ]

Aber beide sind nicht in der Lage, die Transaktion ordnungsgemäß zu signieren, und es tritt ein Fehler auf, während ich sie mit der Transaktion validiere.

Ich habe das folgende Skript verwendet, um eine gültige Signatur mit bitcore-lib zu finden:

const bitcoin = require("bitcoinjs-lib");
const bitcore = require("bitcore-lib");

// These are the private keys of paths- 48'/0'/0'/69/0/0 and 48'/0'/0'/96/0/0 of my ledger
// I retrieved them using https://iancoleman.io/bip39/ and my ledger's mnemonic

const privateKeys = [
  new bitcore.PrivateKey(
    "cNKAjjSL5buaP6q7fE375jkt72JAvvoe8RVh2v5TXv6gdjzXwPVX",
    "testnet"
  ),
  new bitcore.PrivateKey(
    "cMmNVwdfid1FnT4LjH4SJ1mZvTEGnMfUxdasGKTrHvD5nCY1UCvR",
    "testnet"
  )
];

const publicKeys = privateKeys.map(bitcore.PublicKey, bitcoin.networks.testnet);
const address = new bitcore.Address(publicKeys, 2); // 2 of 2

console.log("\n\nCreated this Address:", address);
console.log("\n\nPublic keys:", publicKeys);

// This utxo will create the same rawTx as I have used in my code with Ledger
const utxo = {
  address: "2NB9YNZwwKXannuZryo2KfvMNe4jeSNcSp5",
  txid: "0ad5948e66a4acb72e0fc549cb451fdceabd722f17678d0298cb5ffe65be4f6f",
  vout: 1,
  scriptPubKey: "a914c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a987",
  // "script" : new bitcore.Script(address).toHex(),
  satoshis: 128600000
};

const fee = 10000;

const tx = new bitcore.Transaction()
  .from(utxo, publicKeys, 2)
  .fee(fee)
  .to("2N6FbZbJsGHWRpnbu8vrowCfGATKsYxuDf9", 1300000)
  .change("2NB9YNZwwKXannuZryo2KfvMNe4jeSNcSp5");

const txObj = tx.toObject();
console.log("\n\nTransaction object:", txObj);
console.log("\n\nTransaction hash:", tx);

const signature1 = tx.getSignatures(privateKeys[0])[0];
console.log("\n\nSignature1 object:", signature1.toObject());
console.log("\n\nSignature1 hash:", signature1.signature.toString()); // Outputs a DER signature1
console.log("\n\nSignature1 type:", signature1.sigtype);

console.log("\n\nIs valid tx 1:", tx.isValidSignature(signature1));
if (!tx.isValidSignature(signature1)) throw "Not a valid Signature";

Es erzeugt die folgende gültige Signatur:

Signature1 hash: 304402203fb366ffd2840a900abc7ed25e945e4fdcf37679870a6ee45ce0030dd725856e02202d3b1c86458121cf4b79382281eca074a56d8b67a86dc754e2f7c65501f75b0b

Ich denke, es gibt einen Fehler in der aktuellen Version des Ledgers, können Sie mir bitte helfen.

Lösung: Ich habe eine falsche Eingabe für mein Ledger erstellt, ich musste utxos verwenden, um den Eingabe-TX zu erstellen, aber ich habe den endgültigen RawTX verwendet (den ich mit den Texten erstellt habe). Diejenigen, die mit diesem Problem nicht weiterkommen, sollten Folgendes beachten:

In der Ledger-Dokumentation der signP2SHTransaction-Funktion - http://ledgerhq.github.io/ledgerjs/docs/#btc - Inputs: beziehen Sie sich auf die Utxos, die Sie verbrauchen, um Ihr tx zu bilden - outputScriptHex: kann verwendet werden, um alle gewünschten Ausgabeparameter anzugeben wie Gebühren, Adressänderung etc.

Was ist die Bitcoin-Adresse für diese Ableitung, die Sie haben?
Die Adresse für das Skript des Hauptbuchs ist dieselbe wie die in meinem Bitcore-Skript abgeleitete, dh2NB9YNZwwKXannuZryo2KfvMNe4jeSNcSp5
hallo, ist dir das gelungen? da ich bemerkt habe, dass Sie auch eine p2sh(p2ms())-Adresse verwenden, aber ich habe keine Möglichkeit gefunden, eine p2sh(p2ms())-Adresse mit Ledger zu signieren. Das Beispiel im Test von hw-app-btc zeigt nur, wie p2sh(p2wsh(p2ms())) signiert wird. Wenn Sie das erfolgreich tun, darf ich mir Ihr Snippet ansehen?

Antworten (3)

(das ist zu lang für einen Kommentar, also hier im Antwortbereich):

Meine Beobachtung: Ich habe versucht, die drei Sigs auf der Kommandozeile (mit OpenSSL) zu verifizieren. Ich kann die beiden Pubkeys aus Ihrem ersten Codefragment und den tx sehen. Ich habe das rohe, unsignierte TX erstellt, und nur die dritte Signatur ist in Ordnung (mit Pubkey 02a9d50f9817a9 ...) - ich nehme an, das rohe TX in Ihrem ersten Codebeispiel ist irgendwie falsch. Ich habe diesen unsignierten, rohen TX (mit Redeemscript und 0x47 als Länge) verwendet:

01000000016f4fbe65fe5fcb98028d67172f72bdeadc1f45cb49c50f2eb7aca4668e94d50a0100000047522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752aeffffffff0220d613000000000017a9148eaba4fd80f515c78ddbc2509538e37c40ffcf1287904a96070000000017a914c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a9870000000001000000

das ist doppelt sha256'd dies:

bad8b7f175a67e1ecfd857119609646993eb026b2bb507a49ce47b9ba5d321f7

Welches unsignierte Roh-TX verwenden Sie, um Ihre Codefragmente anzumelden? Haben sie den gleichen doppelten sha256-Hash? Ich sehe, dass Ihr Roh-TX "[Version][tx_in count][prev tx id][prev outpoint]490047[redeem script]..." hat, warum ist das "4900"?

(Sie müssen nicht erneut antworten, Sie können Ihre Frage bearbeiten, indem Sie sie bearbeiten.)

Ja, Sie hatten Recht, ich habe eine falsche Eingabe für mein Ledger erstellt. Ich musste utxos verwenden, um den Eingabe-TX zu erstellen, aber ich habe den endgültigen RawTX verwendet (den ich mit den utxos erstellt habe).

Soeben wurde Ihre tx-Transaktion erfolgreich gesendet. Transaktions-ID: 2ef82827b3e80f34a95fd7b9268a1cd6dc81447822927225f310fa9fd573b176

Bitte lesen Sie diesen Artikel mit einer Erklärung, wie man p2sh-multisig signiert

https://medium.com/@support_62391/exploring-bitcoin-signing-p2sh-input-2dde869c5f5c

>>> from pybtc import *
>>> tx = Transaction("01000000016f4fbe65fe5fcb98028d67172f72bdeadc1f45cb49c50f2eb7aca4668e94d50a01000000490047522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752aeffffffff0220d613000000000017a9148eaba4fd80f515c78ddbc2509538e37c40ffcf1287904a96070000000017a914c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a98700000000", testnet=1)
>>> import pprint
>>> pprint.pprint(tx)
{'amount': 128590000,
 'bSize': 188,
 'blockHash': None,
 'blockIndex': None,
 'blockTime': None,
 'coinbase': False,
 'confirmations': None,
 'data': None,
 'fee': None,
 'format': 'decoded',
 'hash':     'bb788dbf706a4e6a2a4fb3d1f19852773be71e98b5819b57b45e6d8c1e341034',
 'lockTime': 0,
 'rawTx':     '01000000016f4fbe65fe5fcb98028d67172f72bdeadc1f45cb49c50f2eb7aca4668e94d50a01000000490047522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752aeffffffff0220d613000000000017a9148eaba4fd80f515c78ddbc2509538e37c40ffcf1287904a96070000000017a914c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a98700000000',
 'segwit': False,
 'size': 188,
 'testnet': 1,
 'time': None,
 'txId': 'bb788dbf706a4e6a2a4fb3d1f19852773be71e98b5819b57b45e6d8c1e341034',
 'vIn': {0: {'scriptSig':     '0047522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752ae',
         'scriptSigAsm': 'OP_0 '
                         '522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752ae',
         'scriptSigOpcodes': **'OP_0 [71]'**, <<<< script sig should be empty before signing
         'sequence': 4294967295,
         'txId': '0ad5948e66a4acb72e0fc549cb451fdceabd722f17678d0298cb5ffe65be4f6f',
         'vOut': 1}},
 'vOut': {0: {'address': '2N6FbZbJsGHWRpnbu8vrowCfGATKsYxuDf9',
          'addressHash': '8eaba4fd80f515c78ddbc2509538e37c40ffcf12',
          'nType': 1,
          'reqSigs': None,
          'scriptPubKey': 'a9148eaba4fd80f515c78ddbc2509538e37c40ffcf1287',
          'scriptPubKeyAsm': 'OP_HASH160 '
                             '8eaba4fd80f515c78ddbc2509538e37c40ffcf12 '
                             'OP_EQUAL',
          'scriptPubKeyOpcodes': 'OP_HASH160 [20] OP_EQUAL',
          'type': 'P2SH',
          'value': 1300000},
      1: {'address': '2NB9YNZwwKXannuZryo2KfvMNe4jeSNcSp5',
          'addressHash': 'c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a9',
          'nType': 1,
          'reqSigs': None,
          'scriptPubKey': 'a914c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a987',
          'scriptPubKeyAsm': 'OP_HASH160 '
                             'c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a9 '
                             'OP_EQUAL',
          'scriptPubKeyOpcodes': 'OP_HASH160 [20] OP_EQUAL',
          'type': 'P2SH',
          'value': 127290000}},
 'vSize': 188,
 'version': 1,
 'weight': 752}



>>> tx["vIn"][0]["scriptSig"]=""
>>> tx.sign_input(0, private_key=["cNKAjjSL5buaP6q7fE375jkt72JAvvoe8RVh2v5TXv6gdjzXwPVX", "cMmNVwdfid1FnT4LjH4SJ1mZvTEGnMfUxdasGKTrHvD5nCY1UCvR"], redeem_script="522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752ae", witness_version=None)
{'format': 'decoded', 'testnet': 1, 'segwit': False, 'txId': '2ef82827b3e80f34a95fd7b9268a1cd6dc81447822927225f310fa9fd573b176', 'hash': '2ef82827b3e80f34a95fd7b9268a1cd6dc81447822927225f310fa9fd573b176', 'version': 1, 'size': 333, 'vSize': 333, 'bSize': 333, 'lockTime': 0, 'vIn': {0: {'txId': '0ad5948e66a4acb72e0fc549cb451fdceabd722f17678d0298cb5ffe65be4f6f', 'vOut': 1, 'scriptSig': '0047304402203fb366ffd2840a900abc7ed25e945e4fdcf37679870a6ee45ce0030dd725856e02202d3b1c86458121cf4b79382281eca074a56d8b67a86dc754e2f7c65501f75b0b01483045022100df9dfe852cd592062e8e22b5e1cad18a2a66e35a19611960fc314b7b36814ff20220404dcb809cb122a2adb8c16c507ed1be8780c08bbb89c2510775fccfa11162af0147522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752ae', 'sequence': 4294967295, 'scriptSigOpcodes': 'OP_0 [71] [72] [71]', 'scriptSigAsm': 'OP_0 304402203fb366ffd2840a900abc7ed25e945e4fdcf37679870a6ee45ce0030dd725856e02202d3b1c86458121cf4b79382281eca074a56d8b67a86dc754e2f7c65501f75b0b01 3045022100df9dfe852cd592062e8e22b5e1cad18a2a66e35a19611960fc314b7b36814ff20220404dcb809cb122a2adb8c16c507ed1be8780c08bbb89c2510775fccfa11162af01 522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752ae'}}, 'vOut': {0: {'value': 1300000, 'scriptPubKey': 'a9148eaba4fd80f515c78ddbc2509538e37c40ffcf1287', 'nType': 1, 'type': 'P2SH', 'addressHash': '8eaba4fd80f515c78ddbc2509538e37c40ffcf12', 'reqSigs': None, 'address': '2N6FbZbJsGHWRpnbu8vrowCfGATKsYxuDf9', 'scriptPubKeyOpcodes': 'OP_HASH160 [20] OP_EQUAL', 'scriptPubKeyAsm': 'OP_HASH160 8eaba4fd80f515c78ddbc2509538e37c40ffcf12 OP_EQUAL'}, 1: {'value': 127290000, 'scriptPubKey': 'a914c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a987', 'nType': 1, 'type': 'P2SH', 'addressHash': 'c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a9', 'reqSigs': None, 'address': '2NB9YNZwwKXannuZryo2KfvMNe4jeSNcSp5', 'scriptPubKeyOpcodes': 'OP_HASH160 [20] OP_EQUAL', 'scriptPubKeyAsm': 'OP_HASH160 c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a9 OP_EQUAL'}}, 'rawTx': '01000000016f4fbe65fe5fcb98028d67172f72bdeadc1f45cb49c50f2eb7aca4668e94d50a01000000da0047304402203fb366ffd2840a900abc7ed25e945e4fdcf37679870a6ee45ce0030dd725856e02202d3b1c86458121cf4b79382281eca074a56d8b67a86dc754e2f7c65501f75b0b01483045022100df9dfe852cd592062e8e22b5e1cad18a2a66e35a19611960fc314b7b36814ff20220404dcb809cb122a2adb8c16c507ed1be8780c08bbb89c2510775fccfa11162af0147522102a9d50f9817a9cf20f3feb7ad4038e88c8bd471e90dfba3a80c2e0bfd79c893122102faf805ea3652cec322dda6f7571d926f359d8abbd73af1512924151edbec90e752aeffffffff0220d613000000000017a9148eaba4fd80f515c78ddbc2509538e37c40ffcf1287904a96070000000017a914c45f1d5dde5c0f7008dd6c228c1702cfdafdf1a98700000000', 'blockHash': None, 'confirmations': None, 'time': None, 'blockTime': None, 'blockIndex': None, 'coinbase': False, 'fee': None, 'data': None, 'amount': 128590000, 'weight': 1332}
>>>

Erstens ist Ihr Fehler, dass scriptSig leer sein sollte, bevor Sie signieren. Ihre scriptSig ist nur 1 Signatur und kein Einlösungsskript innerhalb von scriptSig.

Ich weiß, wie man eine p2sh-Transaktion erstellt (ich hatte Probleme, dies mit meinem Ledger nano S zu tun. Ich habe es jedoch gelöst, indem ich Informationen zu meiner Lösung hinzugefügt habe, indem ich meine Frage bearbeitet habe.

Signatur1-Hash: 304402203fb366ffd2840a900abc7ed25e945e4fdcf37679870a6ee45ce0030dd725856e02202d3b1c86458121cf4b79382281eca074a56d8b67a86dc755e2f7

Dies mag wie eine wirklich triviale Frage erscheinen ... aber was für eine Signatur ist das? Dies sind nicht die rohen Ausgabeinformationen, die Sie über die Leitung senden.

In meinem Szenario habe ich 2 Endpunkte, die eine Transaktion signieren müssen (normale Multisignatur). Ein Endpunkt ist das Nano-Ledger. Der andere ist ein Bitcoin-Core-Knoten. Ich kann diesen "Stil" -Signatur-Hash aus dem Hauptbuch erhalten. Aber was muss ich dem Bitcoin-Core-Knoten geben, um das Signieren der Transaktion abzuschließen. Ich habe meine scriptsig, vout,redeemscript, txids usw., aber ich versuche herauszufinden, was dieser Hash ist.. und wie kann ich ihn zurück an den Bitcoin-Core-Knoten senden, um die Signierung abzuschließen?

Versuchen Sie, applySignature in diesem doc-bitcore.io/api/lib/transaction zu suchen und lesen Sie darüber, Sie werden es verstehen.