Guion. OP_PICK/OP_ROLL

OP_PICK debe tomar el elemento n de la parte posterior de la pila y colocarlo en la parte superior: para la entrada xn ... x2 x1 x0
da xn ... x2 x1 x0 xn

Lo he usado en algunos scripts, pero he notado un comportamiento extraño.

Por ejemplo, usando la herramienta en: http://webbtc.com/script un script de entrada 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 y un script de salida 19 OP_PICK produce un error. Sin embargo, 18 OP_PICK funciona. Para 25 elementos en la pila, debería funcionar en ambos casos. El problema no parece ser el número 19. Si agrego una entrada
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 los scripts funcionan bien.

He intentado mirar el código fuente, pero en https://github.com/bitcoin/bitcoin/blob/ce56f5621a94dcc2159ebe57e43da727eab18e6c/src/script/interpreter.cpp alrededor de la línea 551 encuentro:

            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);

No estoy muy seguro de cómo se implementa OP_PICK. ¿Alguien puede indicarme el código de OP_PICK o explicar los errores?

¿Es eso 18 en decimal o 18 en hexadecimal?
@NickODell: Creo que lo tienes. Parece que el sitio interpreta "18" como hexadecimal.
Realmente parece estar tomando números por encima de 16 como hexadecimales (con OP_16 el entero más alto que puede empujar directamente a la pila). Todavía estoy tratando de averiguar el formato completo de webbtc.com/script y la verificación de datos mínimos para solucionar el problema.

Respuestas (1)

Aquí está ese código fuente, anotado. Cada anotación hace referencia al código que le sigue.


Esto significa que esta parte del código maneja tanto OP_PICK como OP_ROLL. Esto se debe a que OP_PICK y OP_ROLL son muy similares. La única diferencia es que PICK copia y ROLL mueve.

            case OP_PICK:
            case OP_ROLL:
            {

Estos son dos ejemplos de la pila antes y después. Esto a veces se llama una 'condición previa' y una 'condición posterior'.

Este documenta OP_PICK.

Condición previa:xn ... x2 x1 x0 n

Postcondición:xn ... x2 x1 x0 xn

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

Este documenta OP_ROLL.

Condición previa:xn ... x2 x1 x0 n

Condición posterior: ... x2 x1 x0 xn(Tenga en cuenta la falta de xnal principio).

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

Debe tener al menos dos elementos en la pila, o esta operación no tiene sentido.

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

Copie (pero no elimine) el elemento superior de la pila. Interpretarlo como un número. Conviértalo a un número entero de 32 bits, para que sea más fácil trabajar con él.

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

Ahora quite el elemento superior de la pila. (Que debería ser n, recuerda.)

                popstack(stack);

Si nes negativo, falla. Si n es mayor que la pila, falla. (Aquí es donde creo que su secuencia de comandos tiene un error).

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

Copie el valor que buscamos. Recuerde, 1 2 3 0 OP_PICKdebe obtener el elemento justo debajo de n, 3. Además, stacktop(-1)se refiere a la parte superior. Entonces, por lo tanto, necesitamos invertir n y restar 1.

                valtype vch = stacktop(-n-1);

Recuerde, OP_ROLL elimina el elemento de donde estaba originalmente. Este código no se ejecuta para OP_PICK.

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

Ahora agregue el elemento que acabamos de recuperar en la parte superior de la pila.

                stack.push_back(vch);

Hemos terminado de manejar este caso.

            }
            break;

¿Eso ayuda?

Realmente lo hace Pensé que no hacía nada o usaba exactamente el mismo código que OP_ROLL. El código comentado en torno a if (opcode == OP_ROLL) ha sido la clave para mí.