Skript. OP_PICK/OP_ROLL

OP_PICK sollte das n-te Element von der Rückseite des Stapels nehmen und es nach oben bringen: für die Eingabe xn ... x2 x1 x0
ergibt xn ... x2 x1 x0 xn

Ich habe es in einigen Skripten verwendet, aber ich habe ein seltsames Verhalten bemerkt.

Verwenden Sie beispielsweise das Tool in: http://webbtc.com/script , ein Eingabeskript 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 und ein Ausgabeskript 19 OP_PICK erzeugt einen Fehler. 18 OP_PICK funktioniert jedoch. Bei 25 Elementen im Stack sollte es in beiden Fällen funktionieren. Das Problem scheint nicht die Nummer 19 zu sein. Wenn ich eine Eingabe
1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 hinzufüge, funktionieren die Skripte einwandfrei.

Ich habe versucht, mir den Quellcode anzusehen, aber in https://github.com/bitcoin/bitcoin/blob/ce56f5621a94dcc2159ebe57e43da727eab18e6c/src/script/interpreter.cpp um Zeile 551 herum finde ich:

            case OP_OVER:
            {
                // (x1 x2 -- x1 x2 x1)
                if (stack.size() < 2)
                    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
                valtype vch = stacktop(-2);
                stack.push_back(vch);
            }
            break;

            case OP_PICK:
            case OP_ROLL:
            {
                // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
                // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
                if (stack.size() < 2)
                    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
                int n = CScriptNum(stacktop(-1), fRequireMinimal).getint();
                popstack(stack);

Ich bin mir nicht sicher, wie OP_PICK implementiert ist. Kann mir jemand den Code für OP_PICK zeigen oder die Fehler erklären?

Ist das 18 in Dezimalzahl oder 18 in Hexadezimalzahl?
@NickODell: Ich denke, du hast es. Es scheint, dass die Site "18" als Hex interpretiert.
Es scheint wirklich Zahlen über 16 als hexadezimal zu nehmen (wobei OP_16 die höchste Ganzzahl ist, die Sie direkt auf den Stapel schieben können). Versuchen Sie immer noch, das gesamte Format von webbtc.com/script und die minimale Datenüberprüfung herauszufinden, um das Problem zu umgehen.

Antworten (1)

Hier ist dieser Quellcode, kommentiert. Jede Anmerkung bezieht sich auf den Code dahinter.


Dies bedeutet, dass dieser Teil des Codes sowohl OP_PICK als auch OP_ROLL behandelt. Dies liegt daran, dass OP_PICK und OP_ROLL sehr ähnlich sind. Der einzige Unterschied besteht darin, dass PICK kopiert und ROLL bewegt.

            case OP_PICK:
            case OP_ROLL:
            {

Dies sind zwei Beispiele für den Stack vorher und nachher. Dies wird manchmal als „Vorbedingung“ und „Nachbedingung“ bezeichnet.

Dieser dokumentiert OP_PICK.

Voraussetzung:xn ... x2 x1 x0 n

Nachbedingung:xn ... x2 x1 x0 xn

                // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)

Dieser dokumentiert OP_ROLL.

Voraussetzung:xn ... x2 x1 x0 n

Nachbedingung: ... x2 x1 x0 xn(Beachten Sie das Fehlen von xnam Anfang.)

                // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)

Sie müssen mindestens zwei Elemente auf dem Stack haben, sonst macht diese Operation keinen Sinn.

                if (stack.size() < 2)
                    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

Kopieren Sie das oberste Element auf dem Stapel (aber entfernen Sie es nicht). Interpretiere es als Zahl. Konvertieren Sie es in eine 32-Bit-Ganzzahl, damit es einfacher ist, damit zu arbeiten.

                int n = CScriptNum(stacktop(-1), fRequireMinimal).getint();

Entfernen Sie nun das oberste Element auf dem Stapel. (Was n sein sollte, denken Sie daran.)

                popstack(stack);

Wenn nnegativ, fehlschlagen. Wenn n größer als der Stack ist, fehlschlagen. (Hier denke ich, dass Ihr Skript einen Fehler hat.)

                if (n < 0 || n >= (int)stack.size())
                    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

Kopieren Sie den gesuchten Wert. Denken Sie daran, 1 2 3 0 OP_PICKsollte das Element direkt unter n, 3 erhalten. Bezieht sich auch stacktop(-1)auf die Spitze. Also müssen wir n invertieren und 1 subtrahieren.

                valtype vch = stacktop(-n-1);

Denken Sie daran, dass OP_ROLL das Element dort löscht, wo es ursprünglich war. Dieser Code wird für OP_PICK nicht ausgeführt.

                if (opcode == OP_ROLL)
                    stack.erase(stack.end()-n-1);

Fügen Sie nun das Element hinzu, das wir gerade wieder oben auf dem Stapel erhalten haben.

                stack.push_back(vch);

Wir sind fertig mit diesem Fall.

            }
            break;

Hilft das?

Das tut es wirklich. Ich dachte, entweder hat es nichts getan oder genau den gleichen Code wie OP_ROLL verwendet. Der kommentierte Code um if (opcode == OP_ROLL) war für mich der Schlüssel.