Zeugensignatur erstellen: non-mandatory-script-verify-flag (Signatur muss null sein für fehlgeschlagene CHECK(MULTI)SIG-Operation)

Also versuche ich, meine eigenen Zeugenunterschriften zu machen, wie in bip143 beschrieben. Ich habe Code, der die Beispielsignatur korrekt überprüft, also dachte ich, ich hätte die grundlegende Signierung und Überprüfung im Griff. Ich habe Code, der die Signaturen in bip143 korrekt überprüfen kann, und mit demselben Code verifiziere ich diese Ausgabe korrekt, daher bin ich mir nicht sicher, wie ich feststellen kann, was falsch läuft. Der Fehler, den ich bekomme, ist

error code: -26
error message:
non-mandatory-script-verify-flag (Non-canonical DER signature)

Ich vermute, dass ich die Byte-Reihenfolge in etwas nicht umkehre, aber ich bin mir wirklich nicht sicher, wie ich dies weiter diagnostizieren soll.

----unsignierte Transaktion-------
020000000102303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda400000000000ffffffff02400d03000000000016001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a010000001600140f54e16bd59da94840741b69ab9b9db99467a85800000000
-------unsignierte Transaktion beenden------
dhash(prevouts)=02303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda4000000000
dhash(sequenzen)=ffffffff
dhash (Ausgänge) = 400d030000000000001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a0100000000140f54e16bd59da94840741b69ab9b9db99467a858
Version: 02000000
hashPrevouts: 9ebc8589966e2dd13cd64af1835262c2d8e931b4388a8ebe76620814b4f407bd
Hash-Sequenz: 3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044
Endpunkt: 02303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda4000000000
scriptCode: 1976a914992fda25fdf8447092aa223a34bedd3572173d8688ac
Betrag: 00f2052a01000000
nSequenz: ffffffff
Hash gibt a54e6a8744fa773a5f1c64d7f60d53efd69f8d76e85e7817970a87990874e4c1 aus
nSperrzeit 00000000
nHashType 01000000
pre hash image: 020000009ebc8589966e2dd13cd64af1835262c2d8e931b4388a8ebe76620814b4f407bd3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e7066504402303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda40000000001976a914992fda25fdf8447092aa223a34bedd3572173d8688ac00f2052a01000000ffffffffa54e6a8744fa773a5f1c64d7f60d53efd69f8d76e85e7817970a87990874e4c10000000001000000
gestricheltes Bild: 4f8fce636bb4a44aa6a241e9597bbe54fce3bfe3426a49a94c5b006d42eac007
Rawsig: 2cbb748d06a1b87c933a3118d8c2a5a0ab02111bae777a9e52ba8571eee2d549e80a6e4cef24a172e64d11aa7b83c8005fe6fc078307b9e663ef9eb14fce05a3
der sig: 304402202cbb748d06a1b87c933a3118d8c2a5a0ab02111bae777a9e52ba8571eee2d5490220e80a6e4cef24a172e64d11aa7b83c8005fe6fc078307b9ebe663fce05a307b9ebe663fce
Pub-Schlüssel des Skripts: 992fda25fdf8447092aa223a34bedd3572173d86
Pub-Schlüssel: 0392ff36d0ae9f3a74c0483fd309ff9144972b1dce6d6dfe4d9de474c721b36521
----unterzeichnete Transaktion-------
0200000000010102303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda400000000000ffffffff02400d03000000000016001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a010000001600140f54e16bd59da94840741b69ab9b9db99467a8580247304402202cbb748d06a1b87c933a3118d8c2a5a0ab02111bae777a9e52ba8571eee2d5490220e80a6e4cef24a172e64d11aa7b83c8005fe6fc078307b9e663ef9eb14fce05a301210392ff36d0ae9f3a74c0483fd309ff9144972b1dce6d6dfe4d9de474c721b3652100000000
-------signierte Transaktion beenden------

Dies läuft auf meinem Regtest. Ich kann alle zusätzlichen Informationen bereitstellen, die die Leute für hilfreich halten könnten. Ich habe viele Stunden damit verbracht, alles Mögliche mit allen Beispielen zu vergleichen, bevor ich hier gepostet habe. Ich weiß, es heißt non connical der sig, aber bei meiner Recherche sagen andere Leute, dass Fehler passieren, wenn viele verschiedene Dinge schief gehen. Auch wenn ich die Signatur mit den signierten Cores vergleiche, stimmen sie im Format genau mit "30440220" überein, dann 32 Bytes, dann 0220, dann die anderen 32 Bytes. Das einzige, was anders ist, sind die tatsächlichen Signaturdaten. Außerdem erhalte ich den gleichen Fehler, wenn ich eine native konische DER-Ausgabe verwende. Jede Hilfe wird wie immer sehr geschätzt
-----UPDATE-----
Das Problem mit dem obigen Skript besteht also hauptsächlich darin, dass es nicht korrekt DER-codiert ist, was bedeutet, dass wenn s oder r das meiste MSB > 0x80 ist, es als negativ behandelt wird, da es sich um eine vorzeichenbehaftete Ganzzahl handelt. In diesem Beispiel ist das MSB von S 0xE8, was größer als 0x80 ist. bitcoin hat eine richtlinie, dass es tatsächlich kleiner als der ecdsa-kurvenwert "n" sein muss. Zuerst unten ist der Code, der das Problem behebt, und ich kann die Signatur überprüfen (aber der Fehler existiert immer noch im Bitcoin-Kern). Code zum Invertieren von S in Python:

n=115792089237316195423570985008687907852837564279074904382605163141518161494337
s = bytearray(s_bytes)
sint = 0
cnt = 31
for bb in s:
    sint += bb << cnt*8
    cnt -= 1
if sint >= n/2:
    print("Negated")
    sint = n-sint
    s = bytearray(sint.to_bytes(32, byteorder='big'))

Nicht schlecht. Und was r betrifft, signiert der Bitcoin-Kern einfach neu, bis er niedriger als 0x80 ist. es ist zulässig, dass es höher ist, wenn der Encoder das MSB korrekt auffüllt). Habe es genauso gemacht. Unten ist eine neue Transaktion, bei der das Problem behoben wurde, obwohl ich immer noch den gleichen Fehler bekomme :-/.

----unsignierte Transaktion-------
020000000102303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda400000000000ffffffff02400d03000000000016001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a010000001600140f54e16bd59da94840741b69ab9b9db99467a85800000000
-------unsignierte Transaktion beenden------
dhash(prevouts)=02303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda4000000000
dhash(sequenzen)=ffffffff
dhash (Ausgänge) = 400d030000000000001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a0100000000140f54e16bd59da94840741b69ab9b9db99467a858
Version: 02000000
hashPrevouts: 9ebc8589966e2dd13cd64af1835262c2d8e931b4388a8ebe76620814b4f407bd
Hash-Sequenz: 3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044
Endpunkt: 02303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda4000000000
scriptCode: 1976a914992fda25fdf8447092aa223a34bedd3572173d8688ac
Betrag: 00f2052a01000000
nSequenz: ffffffff
Hash gibt a54e6a8744fa773a5f1c64d7f60d53efd69f8d76e85e7817970a87990874e4c1 aus
nSperrzeit 00000000
nHashType 01000000
pre hash image: 020000009ebc8589966e2dd13cd64af1835262c2d8e931b4388a8ebe76620814b4f407bd3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e7066504402303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda40000000001976a914992fda25fdf8447092aa223a34bedd3572173d8688ac00f2052a01000000ffffffffa54e6a8744fa773a5f1c64d7f60d53efd69f8d76e85e7817970a87990874e4c10000000001000000
gehashtes Bild: 4f8fce636bb4a44aa6a241e9597bbe54fce3bfe3426a49a94c5b006d42eac007
Rawsig: 38d610fa975440d09b501864185e656546015ee98dd8b5b58eb41be547b6a686be784798307a123bdfefce7463b6e7203b69d18950ea95ae684dddb5a9bcaeaf
s vorher: be784798307a123bdfefce7463b6e7203b69d18950ea95ae684dddb5a9bcaeaf
Negiert
s nach: 4187b867cf85edc42010318b9c4918de7f450b5d5e5e0a8d578480d726799292
der sig: 3044022038d610fa975440d09b501864185e656546015ee98dd8b5b58eb41be547b6a68602204187b867cf85edc42010318b9c4918de7f450b5d5e5e0a8d5784.90d7267
Pub-Schlüssel des Skripts: 992fda25fdf8447092aa223a34bedd3572173d86
Pub-Schlüssel: 0392ff36d0ae9f3a74c0483fd309ff9144972b1dce6d6dfe4d9de474c721b36521
----unterzeichnete Transaktion-------
0200000000010102303e285bb228344346acb5292397a761103efcd0182c4cf95b4d01b2bdda400000000000ffffffff02400d03000000000016001453f0b90d7be69d4f5f1356908a9e12fef93561df74e0022a010000001600140f54e16bd59da94840741b69ab9b9db99467a85802473044022038d610fa975440d09b501864185e656546015ee98dd8b5b58eb41be547b6a68602204187b867cf85edc42010318b9c4918de7f450b5d5e5e0a8d578480d72679929201210392ff36d0ae9f3a74c0483fd309ff9144972b1dce6d6dfe4d9de474c721b3652100000000
-------signierte Transaktion beenden------

Auch hier ist Hilfe willkommen

Das ist keine gültige DER-Codierung. Wenn das oberste (erste) Byte >= 0x80 ist, wird die dargestellte Zahl als negativ interpretiert. Wenn der codierte Wert größer als 2^255 ist, bedeutet dies, dass ein zusätzliches 0-Byte vorangestellt werden muss. Das Bitcoin-Netzwerk hat tatsächlich eine zusätzliche Einschränkung, die erfordert, dass der S-Wert in der Signatur unter n/2 liegt. Das ist sehr einfach: wenn s >= n/2, negiere es (ersetze es durch ns).
Sorry musste ich ausprobieren. ok, als ich die native kanonische DER-Formatierung verwendete, hatte sie manchmal die führende 0 und war eins länger. Um sicherzugehen, dass ich es verstehe, im Grunde genommen, wenn es größer als 0x80 ist, anstatt nur die 00 hinzuzufügen, negieren wir stattdessen das gesamte S ? also wenn s[0] > 0x80, s = 0xFFFFFFFF...-s?
@PieterWuille Ich habe versucht, was ich für richtig hielt. noch kein erfolg. S zu negieren, wenn MSB 1 ist, und die führende Null nicht hinzuzufügen, wird jetzt im Allgemeinen nicht validiert. Woher weiß Bitcoin Core, ob Sie es invertiert haben oder nicht?
Der Grund dafür ist Formbarkeit: Wir wollen nicht, dass Dritte eine Transaktion weiterleiten, um sie so zu ändern, dass sie sich ändert, ohne sie jedoch ungültig zu machen. Es stellt sich heraus, dass für ECDSA, wenn (r,s) eine gültige Signatur ist, dies auch (r,ns) ist. Um diese Manipulation zu vermeiden, hat Bitcoin Core eine Richtlinienregel, dass es nur Transaktionen mit Signaturen weiterleitet, deren s-Wert < n/2 ist. Es muss nicht bekannt sein, ob es negiert wurde oder nicht, da beide Formen gleichermaßen gültige ECDSA sind.
Beachten Sie, dass n eine ziemlich nicht triviale Zahl ist, es ist etwa 2 ^ 256 - 1,5 * 2 ^ 128 (Suche nach secp256k1-Kurvenreihenfolge) und R und S und in Big-Endian-Notation codiert.
ok ich habe sn bekommen und mein testvalidierungsprogramm kann die signatur noch validieren. Für Bitcoin Core, was ist mit r? Wenn es zu groß für den Maximalwert ist, sind wir in Ordnung, indem wir einfach die führende 0 hinzufügen? und nach einem "default" der Regelung?
Ja, R muss nur im Bereich [0,n) liegen. Da jedoch eine Wahrscheinlichkeit von 50 % besteht, dass der Wert unter 2^255 liegt, können Sie oft ein Byte sparen, indem Sie einfach versuchen, erneut zu signieren. Das tut Bitcoin Core, aber nur aus finanziellen Gründen (Gebührenstaffelung mit Signaturgröße); Es ist völlig legal, 33-Byte-R-Werte zu haben.
@PieterWuille ok Also habe ich das Beispiel mit den Korrekturen aktualisiert. Es wird immer noch gut decodiert, und ich kann die Signatur immer noch selbst validieren. senddrawtransaction hat immer noch den gleichen Fehler :-/
Was ist jetzt Ihre DER-codierte Signatur?
3044022038d610fa975440d09b501864185e656546015ee98dd8b5b58eb41be547b6a68602204187b867cf85edc42010318b9c4918de7f450b5d5e5e0a8d578470d726 weil die Signatur der Zufälligkeit I929a gemein ist. Ich denke, alles andere sollte gleich sein. Im Update habe ich nur der Vollständigkeit halber das Original und das negierte S gepostet
Das sieht korrekt DER-kodiert aus. Wenn Sie immer noch das gleiche Problem haben, habe ich keine Ahnung.
@PieterWuille in Ordnung, ich schätze die Hilfe/Überprüfung. Danke

Antworten (1)

ok habs kapiert! nach etwa einer Woche :-D. Ich möchte dies posten, weil ich die letzte Woche damit verbracht habe, den Bitcoin-Kern zu lernen, die intermittierenden Werte während des Seufzers zum Vergleich auszugeben. Jeder andere, der wirklich verstehen muss, wie der Kern funktioniert, wird hoffentlich wirklich hilfreich sein. Also zuerst mein spezifisches Problem: Mein Ausgabeskript war einfach völlig falsch, ich glaube, ich habe das Sperr- und das Entsperrskript verwechselt, was die Signatur immer noch nicht ungültig machen sollte, aber dann bemerken Sie auch, dass die Ausgabe im Beispiel so ist:

202cb20600000000 1976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac

Nun, das erste 0x19 ist nicht Teil des öffentlichen Schlüssels des Skripts (die Art und Weise, wie die Rohtransaktion angezeigt wird). Also in der Beschreibung wo es steht

Wenn der Sighash-Typ weder SINGLE noch NONE ist, ist hashOutputs das doppelte SHA256 der Serialisierung aller Ausgabemengen (8-Byte Little Endian) mit scriptPubKey (serialisiert als Skripte in CTxOuts);

Sie hashen mit dem Längenpräfix 0x19. Ich habe die Länge in die Gesamttransaktion aufgenommen, sodass sie immer noch gut dekodiert wurde, aber nicht so, wie ich sie gehasht habe. Einfaches Missverständnis.

Unten ist, wie ich den Kern (0.21.1) gebaut und modifiziert habe, um die Werte auszugeben, um dies herauszufinden

  1. Ich bin hauptsächlich diesem Beispiel https://jonatack.github.io/articles/how-to-compile-bitcoin-core-and-run-the-tests gefolgt und habe im Grunde die meisten Abhängigkeiten aus dieser Liste installiert
  2. export BDB_PREFIX='/home/ubuntu/btc/bitcoinsource/bitcoin-0.21.1/db4'
  3. ./configure BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" BDB_CFLAGS="-I${BDB_PREFIX}/include" --enable-debug
  4. machen
  5. Beachten Sie die Verwendung von --enable-debug. Ich denke, das war nötig, damit Sie gdb verwenden können? Ich habe gelegentlich Thread-Lock-Fehler in der Protokolldatei erhalten, die dazu führten, dass sie im Debug-Modus abstürzte, wenn die Wallet-API verwendet wurde.
  6. Ich habe einen separaten bin-Ordner mit einigen symbolischen Links zu den 4 wichtigsten ausführbaren Binärdateien erstellt. bearbeitete dann /etc/environment, um den Pfad zu diesem Ordner anzuhängen. dann lief "source /etc/environment". Auf diese Weise können Sie die Binärdateien von überall ausführen.
  7. Jetzt können Sie bitcoind -regtest -daemon ausführen. Von hier aus können Sie in den Quellcode gehen, in dem die Binärdateien erstellt wurden, gdb ausführen und an den Prozess anhängen.
  8. Ändern Sie <bitcoin_dir>/src/script.interpreter.cpp Oben habe ich einige grundlegende Funktionen geschrieben, um ein paar verschiedene Variablentypen zu nehmen und in Hex zu drucken. Entschuldigung für den schlechten Code, ich bin von Beruf C-Person, nicht C++. Ich habe eine separate Protokolldatei erstellt, damit ich nicht mit dem Hauptprotokoll von Bitcoin herumgespielt habe
std::string hexhalftostr(unsigned char val)
{
    wenn (wert <= 9)
        Rückgabe std::to_string(val);
    sonst if(val ==10)
        gib "A" zurück;
    sonst if(val ==11)
        gib "B" zurück;
    sonst if(val ==12)
        gib "C" zurück;
    sonst if(val ==13)
        gib "D" zurück;
    sonst if(val ==14)
        gib "E" zurück;
    sonst if(val ==15)
        gib "F" zurück;
    Sonst "" zurückgeben;
}
void logbytes(unsigned char *data, int len)
{
    std::ofstream meinedatei;
    myfile.open ("/home/ubuntu/btc/bitcoinsource/bitcoin-0.21.1/special.log", std::ios_base::app);
    std::string ss = "";
    für (int i = 0;i < len;i++)
    {
        Zeichen lsb, msb;
        lsb = 0x0f & Daten[i];
        msb = 0x0f & (Daten [i] << 4);
        ss += hexhalftostr(msb) + hexhalftostr(lsb);
    }
    meinedatei << ss;
    meinedatei.close();
}
void logbigint (uint256-Daten)
{
    Logbytes (data.data(), 32);
}
void logint (unsigned int data)
{
    unsigned char arrayOfByte[4];
    für (int i = 0; i < 4; i++)
        arrayOfByte[i] = (Daten << (i * 8));
    Logbytes (ArrayOfByte, 4);
}
void loglong (unsignierte lange Daten)
{
    unsigned char arrayOfByte[8];
    für (int i = 0; i < 8; i++)
        arrayOfByte[i] = (Daten << (i * 8));
    Logbytes (arrayOfByte, 8);
}
void logtext(std::string ss)
{
    std::ofstream meinedatei;
    myfile.open ("/home/ubuntu/btc/bitcoinsource/bitcoin-0.21.1/special.log", std::ios_base::app);
    meinedatei << ss;
    meinedatei.close();
}

  1. SignatureHash ist die Funktion, mit der wir sie tatsächlich ausgeben können, während sie berechnet werden
logtext("\nnVersion: ");
        logint(txTo.nVersion);
        logtext("\nhashPrevouts: ");
        logbigint(hashPrevouts);
        logtext("\nhashSquence: ");
        logbigint(hashFolge);
        logtext("\noutpoint: ");
        logbigint(txTo.vin[nIn].prevout.hash);
        logint(txTo.vin[nIn].prevout.n);
        // Ich kann nicht herausfinden, wie ich die Bytes abrufen soll
        ////logtext("\nscriptCode: ");
        ////logbigint(scriptCode);
        logtext("\namount: ");
        loglong(Menge);
        logtext("\nnSequenz: ");
        logint(txTo.vin[nIn].nSequence);
        logtext("\nhashAusgaben: ");
        Logbigint (Hash-Ausgaben);
        logtext("\nnSperrzeit: ");
        logint(txTo.nLockTime);
        logtext("\nhashType: ");
        logint(nHashType);

Und die Rückgabe der Funktion musste ersetzt werden, weil der sha256-Hash die Daten so verbraucht

 return ss.GetHash();

Wird

 uint256 sighash = ss.GetHash();
 logtext("\nhashed image: ");
 logbigint(sighash);
 logtext("\n");
 //return ss.GetHash();
 return sighash;
  1. Ich würde empfehlen, einen Haltepunkt zu setzen und die Funktion "EvalScript" zu durchlaufen. Hier passiert die ganze Magie und ist super gut, um sich darüber zu informieren, wie das eigentliche Bitcoin-Skript läuft, denn jeder Fehlercode umfasst praktisch unbegrenzte Fehler, ich bin mir nicht sicher, ob es eine andere Möglichkeit gibt, Ihren Code zu debuggen, ohne dies zu tun.