Ich versuche, das Bitcoin-Protokoll zu lernen, indem ich es implementiere, und ich habe OP_CHECKSIG
funktioniert (Anweisungen in https://en.bitcoin.it/wiki/OP_CHECKSIG befolgen ), aber ich kann keine Transaktionseingabe mit OP_CHECKMULTISIG
to pass erhalten Signaturprüfung.
Insbesondere stecke ich bei der zweiten tx_input der Transaktion fest eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb , und ich vermute, dass der Schuldige subscript ist (derjenige, der das Signaturskript in der aktuellen Transaktion ersetzt).
So wird der Index in meiner Implementierung berechnet.
Generieren Sie zuerst das Skript durch Verketten [sigScript][OP_CODESEPARATOR][pubkeyScript]
(ich verstehe, dass sich die Skriptausführung daraus entwickelt hat).
Ein (geparstes) Skript für die Eingabe würde also so aussehen:
Frame 00: 0(00) Frame 01: 0000 - 30 44 02 20 27 6d 6d ad 3d ef a3 7b 5f 81 ad d3 0010 - 99 2d 51 0d 2f 44 a3 17 fd 85 e0 4f 93 a1 e2 da 0020 - ea 64 66 02 02 20 0f 86 2a 0d a6 84 24 93 22 ce 0030 - b8 ed 84 2f b8 c8 59 c0 cb 94 c8 1e 1c 53 08 b4 0040 - 86 81 57 a4 28 ee 01 END Frame 02: OP_CODESEPARATOR(0xab) Frame 03: 1(0x51) Frame 04: 0000 - 02 32 ab dc 89 3e 7f 06 31 36 4d 7f d0 1c b3 3d 0010 - 24 da 45 32 9a 00 35 7b 3a 78 86 21 1a b4 14 d5 0020 - 5a END Frame 05: 1(0x51) Frame 06: OP_CHECKMULTISIG(0xae) Frame 07: OP_CODESEPARATOR(0xab) Frame 08: 0000 - 2a 9b c5 44 7d 66 4c 1d 01 41 39 2a 84 2d 23 db 0010 - a4 5c 4f 13 END Frame 09: OP_NOP2/OP_CHECKLOCKTIMEVERIFY(0xb1) Frame 10: OP_DROP(0x75)
Dann erstelle ich den Index vom nächsten OP_CODESEPARATOR
vor dem OP_CHECKMULTISIG
, den ich bis zum Ende des Skripts ausführe. Also schließe ich Frame 03 bis Frame 10 ein.
Dann entferne ich andere OP_CODESEPARATOR
s aus dem Skript, also Frame 07.
Das bedeutet, dass diese Frames den Index bilden sollten:
Frame 03: 1(0x51) Frame 04: 0000 - 02 32 ab dc 89 3e 7f 06 31 36 4d 7f d0 1c b3 3d 0010 - 24 da 45 32 9a 00 35 7b 3a 78 86 21 1a b4 14 d5 0020 - 5a END Frame 05: 1(0x51) Frame 06: OP_CHECKMULTISIG(0xae) Frame 08: 0000 - 2a 9b c5 44 7d 66 4c 1d 01 41 39 2a 84 2d 23 db 0010 - a4 5c 4f 13 END Frame 09: OP_NOP2/OP_CHECKLOCKTIMEVERIFY(0xb1) Frame 10: OP_DROP(0x75)
Serialisiert sieht der letzte Index so aus:
0000 - 51 21 02 32 ab dc 89 3e 7f 06 31 36 4d 7f d0 1c 0010 - b3 3d 24 da 45 32 9a 00 35 7b 3a 78 86 21 1a b4 0020 - 14 d5 5a 51 ae 14 2a 9b c5 44 7d 66 4c 1d 01 41 0030 - 39 2a 84 2d 23 db a4 5c 4f 13 b1 75
Wenn dieses Subskript jedoch verwendet wird, um das Signaturskript zu ersetzen und schließlich Hash zu generieren, meldet die Funktion ecdsa_verify, dass die Signaturüberprüfung fehlschlägt (aber die Eingaben zulässig sind).
Da das gleiche Verfahren für funktioniert OP_CHECKSIG
, habe ich wahrscheinlich etwas Offensichtliches übersehen, aber ich konnte dies beim Lesen des ursprünglichen Satoshi-Codes, der aktuellen Implementierung oder des Bitcoin-Wikis nicht herausfinden.
Wenn es hilft, mein Code für OP_CHECKMULTISIG ist hier: https://github.com/blaesus/tinybtc/blob/checkmultisig/src/script.c#L687
Eine einfachere Version (mit prevTx.pubkeyScript als Index) liefert auch keine gültigen Ergebnisse: https://github.com/blaesus/tinybtc/blob/validate/src/script.c#L344
Die Hauptverwirrung hier kommt daher:
Generieren Sie zunächst das Skript durch Verketten
[sigScript][OP_CODESEPARATOR][pubkeyScript]
Dann fügst du hinzu:
Ich verstehe, dass sich die Skriptausführung daraus entwickelt hat
Ich bin mir nicht sicher, ob Sie "Baut auf dieser Methode auf" oder "Hat diese Methode seitdem geändert" meinen, aber wenn es ersteres ist, könnte dies die unerwarteten Ergebnisse erklären, die Sie erhalten. Hoffentlich kann meine Antwort das klären.
Es ist wahr, dass die Skriptauswertung von Bitcoin für einen (relativ) kurzen Zeitraum so durchgeführt wurde, wie Sie es erwähnt haben, wo ein CODESEPARATOR
dazwischen gesetzt wurde scriptSig
und scriptPubkey
das Ganze dann zu einem einzigen Skript verkettet wurde, das ausgeführt werden sollte, aber das wurde ziemlich früh geändert in Verpflichtung 6ff5f718b6a67797b2b3bab8905d607ad216ee21
, die bis zum 31. Juli 2010 datiert.
Nach dieser Änderung, die eine Funktion mit dem Namen VerifyScript
in einführte script.cpp
(heute ist die Logik in interpreter.cpp
), scriptSig
und scriptPubkey
nicht mehr mit einem CODESEPARATOR
zwischen ihnen verkettet sind. Vielmehr werden sie als zwei getrennte Skripte nacheinander ausgeführt, wobei der Inhalt des Stacks vom ersten zum zweiten übertragen wird.
Der Unterschied ist subtil und ändert den Fluss der Standardeinlösungen nicht wirklich , wird aber bemerkbar, wenn eine CHECKSIG
Operation innerhalb der durchgeführt wird scriptSig
.
Beginnend mit der Finanzierungstransaktion
b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d
Wir sehen, dass die zweite Ausgabe darin eine Zahlung an Folgendes scriptPubKey
bei index ist 1
:
14 2a9bc5447d664c1d0141392a842d23dba45c4f13
NOP2
DROP
OP_CLTV
ist noch nicht aktiv, also ist der Opcode 0xb1
immer noch NOP2
.
Die wird dann durch eingelöst
eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb
durch die Eingabe bei Index 1
:
0
47
30
44
02
20 276d6dad3defa37b5f81add3992d510d2f44a317fd85e04f93a1e2daea646602
02
20 0f862a0da684249322ceb8ed842fb8c859c0cb94c81e1c5308b4868157a428ee
01
CODESEPARATOR
1
21 0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a
1
CHECKMULTISIG
In der kurzen Zeit vor dem Commit 6ff5f718
würden die beiden Skripte mit einem CODESEPARATOR
zwischen ihnen stehenden verknüpft, um das vollständige Skript zu bilden, dann würde die Ausführung beginnen. Wir beginnen mit einem leeren Stapel und einem Zeiger auf den Anfang des Skripts, der den Anfang dessen markiert, was signiert wäre scriptCode
:
0
Auf den Stapel schiebenCODESEPARATOR
- markieren Sie diesen Punkt im Skript als Beginn von scriptCode
(Überschreiben des vorherigen Werts, der am Anfang festgelegt wurde)1
Auf den Stapel schieben1
Auf den Stapel schiebenCHECKMULTISIG
An dieser Stelle müssen wir die zu signierende scriptCode
erstellen. Die Regeln sind:
CODESEPARATOR
bis zum Ende des SkriptsCODESEPARATORS
Da die Signatur eigentlich vor dem letzten CODESEPARATOR
steht, greift Regel #3 hier nicht, wohl aber Regel #1 und #2. Damit bleibt uns folgendes übrig scriptCode
:
1
21 0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a
1
CHECKMULTISIG
14 2a9bc5447d664c1d0141392a842d23dba45c4f13
NOP2
DROP
Das haben Sie sich ursprünglich ausgedacht, aber jetzt wollen wir sehen, wie sich die Änderung bei 6ff5f718
auswirkt.
Denken Sie daran, dass wir es jetzt mit zwei separaten Skripten zu tun haben, aber keine der anderen Regeln geändert wird. Der einzige Unterschied besteht darin, dass diese beiden Skripte getrennt nacheinander ausgeführt werden und dass der Stack, der von der Ausführung von übrig bleibt, scriptSig
an die Ausführung von weitergegeben wird scriptPubkey
.
Wir beginnen mit der Ausführung scriptSig
und durchlaufen die Schritte 1 bis 7 wie zuvor und erreichen den gleichen Punkt, an CHECKMULTISIG
dem wir die scriptCode
. Da das tatsächlich ausgeführte Skript jetzt nur noch aus dem besteht, was in war scriptSig
, scriptCode
wird es zu:
1
21 0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a
1
CHECKMULTISIG
Wenn diese Signaturprüfung bestanden wird (und das tut sie), wird ein Wert 1
auf den Stack geschoben und die scriptSig
Ausführung wird erfolgreich beendet. Dieser Stack wird nun an die Ausführung von weitergegeben scriptPubkey
(was sehr einfach ist) und schließlich als letzter Wert auf dem Stack belassen, wodurch scriptPubkey
aufgelöst wird True
.
Diese sehr interessante Transaktion zeigt wirklich, wie nicht trivial Commit 6ff5f718
tatsächlich war. Es ist ein großartiges Beispiel.
Eine weitere Kleinigkeit, die zu beachten ist, ist, dass dies scriptPubKey
an sich ein Skript ist, das jeder ausgeben kann. Die Bedingungen dieses Skripts können erfüllt werden, indem einfach ein einziger Druck auf ein Element übergeben wird, das True ergibt (z. B. 1
- 0x51
), und dass die Unterschriftsüberprüfung scriptSig
tatsächlich keine Bedeutung hat und die Gelder überhaupt nicht sichert.
Bearbeiten :
Wenn man sich das genauer ansieht, scheint es, dass diese Transaktion ausdrücklich in BIP-17 erwähnt wird (das inzwischen aufgegeben wurde). Die 20 Bytes in scriptPubkey
sind eigentlich der Hash160 des 1-von-1-Multisig-Skripts selbst:
51210232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a51ae
Dies ist in der Tat eine der beispielhaften BIP-17-Transaktionen, die vom Autor des BIP in die Kette gestellt wurden. Wäre dieser BIP akzeptiert worden, wäre eine solche Transaktion nicht jedermanns Sache. (Sie können weitere Details im BIP selbst sehen)
Ich habe es selbst herausgefunden (indem ich alle Teilzeichenfolgen des Skripts ausprobiert habe).
Der korrekte Index ist dieser:
0000 - 51 21 02 32 ab dc 89 3e 7f 06 31 36 4d 7f d0 1c 0010 - b3 3d 24 da 45 32 9a 00 35 7b 3a 78 86 21 1a b4 0020 - 14 d5 5a 51 ae END
Mit anderen Worten, es ist Frame 03 bis Frame 06:
Frame 03: 1(0x51) Frame 04: 0000 - 02 32 ab dc 89 3e 7f 06 31 36 4d 7f d0 1c b3 3d 0010 - 24 da 45 32 9a 00 35 7b 3a 78 86 21 1a b4 14 d5 0020 - 5a END Frame 05: 1(0x51) Frame 06: OP_CHECKMULTISIG(0xae)
Im Gegensatz zur normalen Subscript-Generierung geht dieses Subscript nicht bis zum Ende des Skripts (es enthält überhaupt keinen Teil des öffentlichen Schlüssels).
Ich bin mir nicht sicher, warum dies der Fall ist.
Pieter Wuille