Meine Frage bezieht sich auf die Änderungen an Bitcoin, die durch den Commit-Bereich vorgenommen wurden [a75560d8, 6ff5f718]
, und ihre Auswirkungen auf den Konsens. Aus dieser Reihe von vier Commits (alle Satoshi zugeschrieben) während der Daten vom 30. bis 31. Juli 2010 sind nur die ersten und letzten Commits für die Frage relevant (die beiden in der Mitte scheinen eine Änderung des Build-Systems zu sein, kein Code).
Die beiden Commits (erster und zweiter von nun an) a75560d8
werden 6ff5f718
der Behebung einiger Schwachstellen in der Semantik und Ausführung von Bitcoin-Skripten zugeschrieben. Zum Beispiel im ersten Commit:
NOP#
stattdessen die verschiedenen Opcodes hinzugefügtOP_RETURN
wurde vom bloßen Beenden des Skripts zum Zurückkehren false
für die gesamte Ausführung geändertIm zweiten Commit:
false
für die Ausführung zurückkehrt*SHIFT
Opcodes wurde vorgenommen (ich verstehe es aber nicht wirklich)VerifyScript
eingeführt, die das Verhalten der Skriptausführung änderteDiese Frage bezieht sich auf diese Liste von Änderungen und ob eine davon möglicherweise nicht abwärtskompatibel ist. Insbesondere werde ich mich auf die Änderung (5) aus dem zweiten Commit konzentrieren: 6ff5f718:script.cpp
Vor dieser Änderung wurden zum Zeitpunkt der Txout-Einlösung die Eingaben einer einlösenden Transaktion scriptSig
und die Prevouts scriptPubKey
zu einem einzigen Skript zusammengefügt OP_CODESEPARATOR
, indem ein zwischen ihnen platziert und das Ergebnis an das übergeben wurde, EvalScript
das das Skript ausführen würde.
Innerhalb EvalScript
wurde ein leerer Stack erstellt und die Ergebnisse der verschiedenen im Skript ausgeführten Operationen werden darauf geschoben. Wenn die Skriptausführung ohne Fehler abgeschlossen wird, wird ein boolescher Wert zurückgegeben; Wenn der Stapel nicht leer ist, wird das oberste Element an eine CastToBool
Funktion übergeben und zurückgegeben ( true
oder false
), und wenn der Stapel leer ist, false
wird zurückgegeben.
Nach der Änderung VerifyScript
umschließt die Funktion zwei separate Aufrufe von EvalScript
, running scriptSig
und scriptPubKey
separat nacheinander. Der leere Stack, der zuvor in erstellt wurde, EvalScript
wird stattdessen in erstellt VerifyScript
, bevor eine Skriptausführung beginnt, und die abschließende Überprüfung des Inhalts des Stacks (oder dessen Fehlen) und CastToBool
wurde auch nach verschoben VerifyScript
, bis zu dem Punkt, nachdem beide scriptSig
ihre scriptPubKey
Ausführung abgeschlossen haben.
In wird VerifyScript
zunächst die scriptSig
von der Einlösetransaktion EvalScript
zusammen mit dem leeren Stapel an übergeben. Die Maschine führt das Skript aus und die Ergebnisse der Operationen werden auf den Stack geschoben, der damit übergeben wurde. Wenn während der Ausführung keine Fehler aufgetreten sind, true
wird zu zurückgegeben VerifyScript
. Zweitens, wenn tatsächlich true
zurückgegeben wurde, scriptPubKey
wird die aus der Finanzierungstransaktion EvalScript
zusammen mit dem Stack an übergeben, der an diesem Punkt den Inhalt enthält, der von scriptSig
der Ausführung übrig geblieben ist. Der scriptPubKey
wird ausgeführt, der den Inhalt des Stapels weiter manipuliert und, wenn keine Fehler aufgetreten sind, zu true
zurückkehrt VerifyScript
. Zuletzt, wenn true
zurückgegeben wurde, dann die Prüfung auf leeren Stapel undCastToBool
abgeschlossen sind, was das Endergebnis der Skriptüberprüfung bestimmt.
Als Grund für diese Änderung wurde die Behebung einer möglichen Schwachstelle in Script : SE answer , BCTalk - Thread genannt . Obwohl es orthogonal zu dieser Frage ist, habe ich deshalb die Liste der Änderungen von commit eingefügt a75560d8
.
Es ist nicht allzu schwer zu erkennen, dass diese Änderung in Anbetracht einfacher Skripte (ohne Prüfzeichen) tatsächlich abwärtskompatibel ist. Im schlimmsten Fall könnten einige Skripte unverbrauchbar werden, aber es scheint keinen Fall zu geben, in dem ein zuvor unverbrauchtes Skript infolge dieser Änderung unverbrauchbar werden könnte.
Auch bei Skripten mit einem Checksig wie und Multisig findet der Opcode immer in statt , was vor dieser Änderung p2pk
durch ein von den Elementen von getrennt war . Anscheinend blieb die Semantik gleich – bis etwa zwei Jahre später.p2pkh
CHECKSIG
scriptPubKey
scriptSig
OP_CODESEPARATOR
OP_CHECKSIG
Wenn Sie sich die Wiki-Seite für wiki-checksig ansehen , erklären die Schritte (2) bis (4), wie Sie von zu wechseln scriptCode
und subScript
was insbesondere passiert, wenn ein OP_CODESEPARATOR
in existiert scriptCode
:
- Ein neues Unterskript wird aus dem erstellt
scriptCode
(dasscriptCode
ist das tatsächlich ausgeführte Skript – entweder dasscriptPubKey
für Nicht-Segwit-, Nicht-P2SH-Skripts oder dasredeemscript
in Nicht-Segwit-P2SH-Skripts). Das Skript unmittelbar nach dem zuletzt analysiertenOP_CODESEPARATOR
bis zum Ende des Skripts ist diesubScript
. Wenn es keine gibt,OP_CODESEPARATOR
wird das gesamte Skript zumsubScript
- Alle Vorkommen von
sig
werden aus subScript gelöscht, falls vorhanden (es ist kein Standard, eine Signatur in einem Eingabeskript einer Transaktion zu haben).- Alle verbleibenden
OP_CODESEPARATORS
werden entferntsubScript
Nun, vor dem Commit 6ff5f718
, wenn scriptSig
und scriptPubKey
zu einem einzigen Skript zusammengefügt wurden, scriptCode
würde das so aussehen:
<scriptSig> CODESEPARATOR <scriptPubKey>
Bei Checksig-Operationen, die in stattfinden scriptPubKey
, subScript
ist alles rechts von CODESEPARATOR
- im Grunde <scriptPubKey>
sich selbst (es sei denn, es gibt mehr Codeseparatoren oder wenn Signaturen, die von einem Checksig verbraucht werden, auch in existieren scriptPubKey
).
Nach dem Commit gäbe es tatsächlich zwei separate Ausführungen mit scriptSig
first und second, wobei jede Ausführung ihre eigene und anschließend ihre eigene scriptPubKey
hätte . Nun, da Checksig-Operationen immer noch nur in ausgeführt werden , scheint es so zu bleiben, aber was würde passieren, wenn ein Opcode in ausgeführt wird ?scriptCode
subScript
scriptPubKey
subScript
CHECK(MULTI)SIG
scriptSig
Am 24. Januar 2012 wurde Block 163685 abgebaut, der die Transaktion enthielt eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb
. Diese Transaktion, die beiden unmittelbar danach und ihre Finanzierung, bei b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d
der ebenfalls im selben Block abgebaut wurde, werden alle in BIP-17 erwähnt , was eine alternative Implementierung der p2sh
Semantik ist:
OP_CHECKHASHVERIFY
definiert den vorhandenenOP_NOP2
Opcode neu und funktioniert bei der Ausführung wie folgt:Hashen Sie zuerst das Ende des vorherigen Skripts (im allgemeinen Fall ;
scriptSig
wenn kein vorheriges Skript vorhanden ist, wird eine Nullzeichenfolge gehasht), beginnend mit dem zuletzt ausgewertetenOP_CODESEPARATOR
(oder ab dem Anfang des Skripts, wenn keinesOP_CODESEPARATOR
vorhanden war).- Vergleichen Sie dies dann mit dem Element oben auf dem Stapel (wenn es keines gibt, schlägt das Skript sofort fehl)
- Wenn die Hashes übereinstimmen, tun Sie nichts, fahren Sie fort, als ob ein
OP_NOP
; Wenn sie nicht übereinstimmen, schlägt das Skript sofort fehl. Beachten Sie, dass im Fall eines übereinstimmenden Hashs das oberste Stack-Element (der Hash, mit dem verglichen wird) nicht vom Stack entfernt wird. Dies dient der Abwärtskompatibilität.
Beachten Sie, dass in bip17 das redeemScript
als tatsächlich ausführbares Skript in angegeben wird scriptSig
und nicht als einzelner Daten-Push eines Blobs, wie es bei bip16 der Fall ist. Auch wenn ältere Knoten nicht erzwingen, dass hash160
of scriptSig
gleich dem 20-Byte-Wert von prevout ist scriptPubKey
, müssten sie es dennoch ausführen und alle CHECK(MULTI)SIG
darin enthaltenen Operationen validieren.
Ich gehe hier davon aus, dass diese bip17-Beispieltransaktionen von Knoten abgebaut und validiert wurden, auf denen Software ausgeführt wurde, die im Januar 2012 neu war, aber nehmen wir an, dass es noch Knoten im Netzwerk gibt, auf denen ältere Software ausgeführt wird, und insbesondere solche, auf denen Versionen und niedriger ausgeführt werden v0.3.6
. Wären diese älteren Knoten in der Lage, sich mit den neueren Knoten über die Gültigkeit von Block 163685 zu einigen?
Wenn wir die Finanzierungstransaktion durchgehen b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d
und die Ausgabe bei Index 1 betrachten, sehen wir die scriptPubKey
( NOP2
ist eigentlich eine nicht aktive OP_CHECKHASHVERIFY
)
0x14 0x2a9bc5447d664c1d0141392a842d23dba45c4f13 NOP2 DROP
Und in der Ausgabentransaktion eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb
haben wir von der Eingabe bei Index 1 diescriptSig
0 0x47 0x30440220276d6dad3defa37b5f81add3992d510d2f44a317fd85e04f93a1e2daea64660202200f862a0da684249322ceb8ed842fb8c859c0cb94c81e1c5308b4868157a428ee01 CODESEPARATOR 1 0x21 0x0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a 1 CHECKMULTISIG
Überlegen Sie zuerst, wie Software von und höher die für die in v0.3.7
konstruieren würde . Wir beginnen mit a , das genau ist , und da a ausgeführt wird, wird es zu:subScript
CHECKMULTISIG
scriptSig
scriptCode
scriptSig
CODESEPARATOR
subScript
1 0x21 0x0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a 1 CHECKMULTISIG
Überlegen Sie nun, wie Software von v0.3.6
und darunter die subScript
für dasselbe erstellen würde CHECKMULTISIG
. Unsere scriptCode
besteht nicht mehr nur aus scriptSig
, sondern aus einer Verbindung von ihr und scriptPubKey
durch eine CODESEPARATOR
. Das scriptCode
würde so aussehen:
0 0x47 0x30440220276d6dad3defa37b5f81add3992d510d2f44a317fd85e04f93a1e2daea64660202200f862a0da684249322ceb8ed842fb8c859c0cb94c81e1c5308b4868157a428ee01 CODESEPARATOR 1 0x21 0x0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a 1 CHECKMULTISIG CODESEPARATOR 0x14 0x2a9bc5447d664c1d0141392a842d23dba45c4f13 NOP2 DROP
Erinnern Sie sich an die Regeln aus dem Wiki. An dem Punkt, an dem CHECKMULTISIG
ausgeführt wird, subScript
ist alles von dem Punkt nach der letzten Ausführung CODESEPARATOR
bis zum Ende des Skripts, wobei alle CODESEPARATOR
s auf der rechten Seite des Prüfzeichenoperators entfernt wurden:
1 0x21 0x0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a 1 CHECKMULTISIG 0x14 0x2a9bc5447d664c1d0141392a842d23dba45c4f13 NOP2 DROP
Wenn dies korrekt ist, scheinen sich alte Knoten mit Versionen v0.3.6
und niedriger nicht darauf einigen zu können, welche sighash
für diese Ausgabetransaktion die richtige ist, aber da ich realistisch gesehen keine solche alte Softwareversion ausführen kann, kann ich mir nicht absolut sicher sein. Ich habe eine aktuelle Bitcoin-Core-Version mit einigen Änderungen gepatcht, die es mir ermöglichten, die Validierung von tx zu simulieren eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb
, und wie erwartet hat sie die Validierung nicht bestanden. Ich werde die beiden Seufzer unten zusammen mit dem öffentlichen Schlüssel und der Signatur zum Vergleich hinzufügen.
Meine Frage ist also , gibt es etwas, das ich falsch verstehe oder vielleicht übersehen habe, das es Prä- v0.3.7
und Postknoten ermöglichen würde v0.3.7
, sich über die Gültigkeit von Block 163685 zu einigen?
(auch relevant: diese SE-Frage )
Pubkey : 0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a
Signature (DER) : 30440220276d6dad3defa37b5f81add3992d510d2f44a317fd85e04f93a1e2daea64660202200f862a0da684249322ceb8ed842fb8c859c0cb94c81e1c5308b4868157a428ee
Signature (r,s) : (276D6DAD3DEFA37B5F81ADD3992D510D2F44A317FD85E04F93A1E2DAEA646602, F862A0DA684249322CEB8ED842FB8C859C0CB94C81E1C5308B4868157A428EE)
Vorseufzen v0.3.7
(bestätigt nicht):
01000000
02
4de8b0c4c2582db95fa6b3567a989b664484c7ad6672c85a3da413773e63fdb8 00000000
00
FFFFFFFF
4de8b0c4c2582db95fa6b3567a989b664484c7ad6672c85a3da413773e63fdb8 01000000
3C 51210232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a51ae142a9bc5447d664c1d0141392a842d23dba45c4f13b175
FFFFFFFF
02
E0FD1C0000000000 19 76a914380cb3c594de4e7e9b8e18db182987bebb5a4f7088ac
C0C62D0000000000 17 142a9bc5447d664c1d0141392a842d23dba45c4f13b175
00000000
01000000
sha256 : 1EB276326D72CB358F6C275D6542F76EED4E36364727CB82D40A116244EBDDB5
sha256d : 11491E74778E1FA8C40CC8E07E1F835677CF1AC81F54255EC1C7125C1894939A
Post- v0.3.7
Seufzer (bestätigt) :
01000000
02
4de8b0c4c2582db95fa6b3567a989b664484c7ad6672c85a3da413773e63fdb8 00000000
00
FFFFFFFF
4de8b0c4c2582db95fa6b3567a989b664484c7ad6672c85a3da413773e63fdb8 01000000
25 51210232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a51ae
FFFFFFFF
02
E0FD1C0000000000 19 76a914380cb3c594de4e7e9b8e18db182987bebb5a4f7088ac
C0C62D0000000000 17 142a9bc5447d664c1d0141392a842d23dba45c4f13b175
00000000
01000000
sha256 : 3858A592C15A47F3058010689883DECCD4AF41F5367B9776429613DFB3339883
sha256d : 8D7AD159644D312664472F90E7B823071B1361725CAC78531569FD836EA90350
Fast alle Commits an den Client vor den Commits, die Sie hier genannt haben, waren Hard Forks aufgrund der Einbeziehung von OP_VER
und OP_VERIF
, selbst wenn die Änderung am Client ansonsten keine inkompatible Änderung war.
case OP_VER:
{
CBigNum bn(VERSION);
stack.push_back(bn.getvch());
}
break;
Ganz einfach, der Opcode wird VERSION
auf den Stack geschoben, was die Versionsnummer des Codes ist, der zur Validierung verwendet wird. Diese Nummer wurde mit jeder Veröffentlichung aktualisiert, sodass keine zwei Versionen vor ihrer Entfernung tatsächlich miteinander kompatibel sind.
Es gibt mehrere andere, die Sie nicht erwähnt haben, wie z. B. die nLockTime
Änderung in eine vorzeichenlose Variable mit einem höhenbasierten Hard Fork, einige geringfügige Änderungen an den Implementierungen der Opcodes und das Entfernen von Double-Byte-Opcodes. Es ist sehr schwierig aufzuzählen, welche dieser Versionen tatsächlich miteinander kompatibel sind, aufgrund der großen Anzahl von Änderungen und der schlechten Werkzeuge, die wir haben, die mit diesen alten Versionen kompatibel sind.
Andreas Chow
Arubi
behkod