Glosario / Referencia para desarrolladores
OP_VERIFY
- ¿Qué es OP_VERIFY?
- OP_VERIFY desapila el elemento de la cima de la pila y aborta todo el script salvo que ese elemento sea verdadero, lo que convierte cualquier comprobación del Script de Bitcoin en un requisito ineludible.
El byte 0x69 es la aserción de uso general del lenguaje. Script puede ramificarse con OP_IF, pero no tiene forma de lanzar un error ni otro valor de retorno que lo que sobreviva en la pila, así que un contrato exige una condición calculando la respuesta y entregándosela a esta instrucción. Casi todos los pagos que se han hecho han pasado por una, normalmente oculta dentro de un opcode (código de operación) combinado.
Cómo funciona
OP_VERIFY elimina un elemento de la pila y plantea una sola pregunta: ¿es verdadero?
Aquí la verdad es una noción a nivel de bytes, no numérica. Un elemento vacío es falso. También lo es una secuencia de bytes cero, y también el cero negativo, una secuencia de ceros que termina en 0x80. Todo lo demás cuenta como verdadero, incluido un hash de 20 bytes o una clave pública de 33 bytes. Si el elemento es falso, el script termina en el acto y la entrada que lo contiene es inválida, lo que invalida la transacción de la que forma parte. Si el elemento es verdadero, no se devuelve nada a la pila y la evaluación pasa al siguiente opcode.
Ese comportamiento es lo que permite a un script comprobar varias cosas en secuencia. Un script solo tiene éxito cuando al final queda exactamente un valor verdadero, así que sin aserciones cada condición tendría que condensarse en un único booleano final. Con ellas, un contrato puede exigir un bloqueo temporal (timelock), luego exigir una preimagen de hash y luego exigir una firma, y fallar a la primera decepción.
Cuatro opcodes llevan la aserción incorporada y existen únicamente para ahorrar un byte: OP_EQUALVERIFY, OP_CHECKSIGVERIFY, OP_CHECKMULTISIGVERIFY y OP_NUMEQUALVERIFY. Como estos cubren los casos habituales, un 0x69 aislado es raro en la mainnet.
Dos opcodes llevan VERIFY en el nombre y no se comportan así en absoluto. OP_CHECKLOCKTIMEVERIFY, del BIP65, y OP_CHECKSEQUENCEVERIFY, del BIP112, se añadieron mediante una bifurcación blanda (soft fork) sobre opcodes NOP que antes carecían de significado, así que, para mantener contentos a los nodos antiguos, tenían que dejar la pila exactamente como la encontraron. Ambos inspeccionan el elemento de la cima y fallan cuando la condición no se cumple, pero ninguno lo consume, y por eso en los scripts reales siempre van seguidos de OP_DROP.
Dónde aparece
Las aserciones se concentran allí donde un script tiene más de una cosa que demostrar.
Una bóveda sencilla con bloqueo temporal se lee como una clave pública, OP_CHECKSIGVERIFY, un retraso como 144 bloques (aproximadamente un día) y después OP_CHECKSEQUENCEVERIFY. Los scripts de compromiso y de HTLC de Lightning se construyen del mismo modo, encadenando una comprobación de preimagen y una verificación de firma para que ninguna de las dos baste por sí sola para mover dinero. Los gastos por ruta de script de Taproot se apoyan mucho en este patrón, ya que tapscript reemplazó OP_CHECKMULTISIG por OP_CHECKSIGADD y usa variantes VERIFY para mantener compactas las políticas de varias claves.
Miniscript, el compilador de políticas que varias billeteras de escritorio ya usan para describir condiciones de gasto de forma legible, emite estos opcodes por usted. Es la forma más sencilla de ver cómo se generan aserciones sin escribir a mano un script en bruto.
OP_VERIFY frente a OP_RETURN
Tanto OP_VERIFY como OP_RETURN terminan mal un script, pero solo uno de ellos hace antes una pregunta. OP_VERIFY falla cuando el valor que recibe es falso, así que un gasto correcto pasa de largo sin dejar rastro. OP_RETURN, el byte 0x6a, falla siempre que se ejecuta, sin ninguna condición asociada, y precisamente por eso se usa para construir salidas que nunca se pueden gastar y para adjuntar datos a una transacción. Un script que muere en el primero recibió algo incorrecto. Un script que muere en el segundo nunca estuvo pensado para ser gastable desde el principio.