Hosted on hyper.media via the Hypermedia Protocol.
Maleabilidad: Del latín "Malleare" (Golpear con un martillo), posibilidad de un material para admitir distintas formas sin quebrarse.
Una firma digital es mucho más que una simple prueba de posesión de una Clave Privada: es una declaración de intenciones. Lo que firmamos —el mensaje exacto— refleja nuestras intenciones de forma precisa. Si alguien modifica ese mensaje, cualquier observador externo podrá comprobar claramente que nuestra intención original ha sido desvirtuada.
Por ejemplo, si firmamos con nuestra Clave Privada el mensaje: "Mi coche es azul", estamos declarando nuestra intención de que quien lo verifique sepa que nuestro coche es azul. Sin embargo, si alguien altera ese mensaje a "Mi coche es rojo", la verificación de la firma fallará, dejando claro que ese cambio no corresponde a nuestras intenciones originales.
En Bitcoin, este principio se traduce en la firma de transacciones. Por ejemplo, podemos firmar la transacción: "Quiero transferir los fondos de las direcciones A y B hacia la dirección C."
Esto refleja nuestra intención de consolidar los fondos de dos direcciones en una nueva, la dirección C.
Ahora bien, ¿qué ocurre si firmamos un mensaje más general, como: "Quiero transferir los fondos de las direcciones A y B" ?
En este caso, dejamos abierta la posibilidad de que alguien más complete el mensaje añadiendo, por ejemplo, "hacia la dirección D" sin que la verificación resulte en error, puesto que ese dato no está vinculado con la firma digital. Esto puede ser útil en algunos casos, pero también implica que nuestra intención original puede ser reinterpretada o moldeada por terceros.
El enfoque de nuestra firma determinará qué partes de la transacción están protegidas contra modificaciones. En la mayoría de los casos, vinculamos la firma a toda la transacción, es decir, a todos los inputs (entradas) y outputs (salidas). Esto garantiza que todas las partes de la transacción sean inamovibles y cualquier alteración posterior invalide la transacción.
¿Qué pasaría si, en lugar de firmar toda la transacción, solo vinculamos nuestra firma a partes específicas, como ciertos inputs o outputs? Aunque inicialmente pueda parecer un sinsentido, esta flexibilidad que Bitcoin nos brinda para decidir que partes de la transacción se firman, nos permite crear transacciones participativas donde los individuos no están necesariamente en el mismo lugar y tiempo, aumentado el grado de personalización de las mismas.
Pudiendo moldearlas a tu gusto, haciéndolas maleables.
Por ejemplo, una transacción participativa sería crear un cheque al portador:
    Firmamos únicamente los inputs de una transacción.
    Dejamos sin firmar los outputs.
Esto permite que quién posea esta transacción incompleta pueda definir posteriormente los outputs, decidiendo a dónde se enviarán los fondos. En este caso, nuestra firma desbloquea los fondos de manera correcta, dejando abierta la posibilidad de que sean gastados según el criterio del portador de ese "cheque bitcoin".
Este artículo es una continuación de mi anterior artículo, y por tanto, te recomiendo encarecidamente leer "Firmas Digitales en Bitcoin - ¿Lo tuyo es tuyo?".
Los indicadores SigHash y la Proto-transacción
Esta personalización de transacciones la vamos a conseguir en el estado de la transacción previo a la firma —la proto-transacción— a través de un elemento clave en Bitcoin: los indicadores SigHash.
El indicador SigHash señaliza qué parte de la transacción ha sido firmada. Este indicador será crucial a la hora de verificar la transacción, puesto que no será lo mismo verificar la firma para toda la transacción que para una parte concreta.
Además, en una misma transacción habrá tantos indicadores SigHash como número de inputs, puesto que cada input tendrá el suyo propio [Nota 1]. De esta forma, el propietario de los fondos bitcoin que se van a enviar y que conforman uno de los inputs de la transacción, podrá decidir que partes de la misma firma y cuales no, demostrando así su intención de hacer unas partes u otras inamovibles.
Por ejemplo, si firma su input con el indicador SigHash ALL, estará indicando que firma toda la transacción. Esto es, su intención será que la transacción que se envíe a la red sea la misma que está firmando (mismos inputs, mismos outputs, misma cantidad...), haciendo inamovibles todas las partes de la transacción. Cualquier cambio a posteriori invalidará la verificación de la firma digital de su input, y, por tanto, la transacción en su conjunto.
El indicador SigHash no solo se incluye en la transacción final, sino también en su versión previa, la proto-transacción:
La proto-transacción, técnicamente conocida como Hash-preimage o simplemente Preimage, es el conjunto de información sobre el que se firmará digitalmente. Este conjunto de información será similar a la transacción final, con algunos cambios:
    No incluye la firma digital (por razones obvias)
    El lugar que en la proto-transacción ocupa el Locking Script a desbloquear, pasa a ser ocupado por la Firma Digital y la Clave Pública (el Unlocking Script) en la transacción final.
    El SigHash cambia de lugar dentro de la composición, aunque se mantiene idéntico.
Cada input tendrá su propio indicador SigHash y aunque todos los inputs compartan el mismo tipo de indicador, la proto-transacción será distinta para cada uno de los inputs. Esto es, el conjunto de información que firma cada una de las Claves Privadas correspondientes a cada uno de los inputs, será ligeramente distinto en cada caso, aunque la transacción final sea la misma.
Esto ocurre porque a la hora de firmar una proto-transacción que contempla la existencia de varios inputs, no se incluirán los Locking Script a desbloquear en los inputs que no sean el que se esté firmando. En su defecto, se incluirá la cadena vacía "00" en hexadecimal.
¿Por qué se diseñó de esta forma? Puede haber varias razones: optimizar el proceso de firma, prevenir errores o evitar que scripts incorrectos sean firmados involuntariamente... aunque la verdad nunca la sabremos: Ask Satoshi
Por otro lado, no será la misma proto-transacción para un input que con su firma digital está vinculando todos los elementos de la transacción (SigHash ALL), que para un input que no firma los outputs (SigHash NONE). En el primer caso se incluirán todas las partes a la hora de firmar, mientras que en el segundo caso solo se incluirán los inputs.
Englobando todo:
    Se construye una proto-transacción que variará levemente según el input que estemos firmando y las partes de la transacción que ese input vaya a vincular a su firma.
    Las Claves Privadas de cada input firmarán la proto-transacción que les corresponda.
    Una vez obtenidas las firmas, se construirá la transacción final incluyendo las firmas.
Una vez se envía la transacción a la red y los nodos comienzan a validarla, es crucial que estos puedan determinar qué partes de la transacción han sido firmadas para verificar correctamente las firmas. Durante este proceso, el nodo identificará las secciones de la transacción que fueron firmadas gracias al indicador SigHash que lleva cada uno de los inputs.
Es por ello que, para validarla, el nodo debe reconstruir cada una de las "proto-transacciones" que fueron firmadas originalmente (teniendo en cuenta el indicador SigHash del input en cuestión), ya que el mensaje firmado por cada Clave Privada corresponde a estas proto-transacciones, no a la transacción final. Este procedimiento ocurre cuando el nodo alcanza el operador OP_CHECKSIG en el paso final de la verificación del unlocking script.
El operador OP_CHECKSIG ordena reconstruir la proto-transacción asociada a cada input, partiendo de la transacción final. El proceso es el siguiente:
    Se crea una copia de la transacción final, denominada txCopy.
    Se modifica esta copia para adaptarla a las características específicas del input que se está verificando, obteniendo así la proto-transacción correspondiente.
Una vez reconstruida la proto-transacción:
    Se calcula su hash utilizando el algoritmo SHA-256 dos veces.
    Este hash, junto con la Clave Pública asociada, se utiliza para verificar la firma digital.
Tipos de SigHash
En total existen tres indicadores SigHash: ALL, NONE y SINGLE. Además, con el modificador ANYONECANPAY se podrán conseguir otros tres tipos más: ALL|ANYONECANPAY, NONE|ANYONECANPAY y SINGLE|ANYONECANPAY; haciendo un total de seis indicadores SigHash.
En una misma transacción, se pueden combinar distintos indicadores SigHash, permitiendo que distintas partes colaboren en procesos de firma progresivos; esto es, un individuo podrá firmar unas partes de la transacción de tal forma que otros individuos completen la transacción con su propia información y decidiendo que partes firman. En definitiva, cada individuo decidirá que partes de la transacción desea que sean inamovibles.
ALL
El indicador SigHash ALL implica que se firman todos los inputs y todos los outputs, provocando que todas las partes sean inamovibles. Está representado por la cadena hexadecimal 01.
Es el indicador más utilizado, tanto que si tenemos en cuenta todas las transacciones hasta el bloque 875507 (20 de diciembre de 2024), este indicador es el que está presente en el 99,79% de los inputs. Y es lógico que esto sea así, puesto que lo más común es que la intención sea que una vez definida y firmada la transacción, no se pueda cambiar nada a posteriori.
NONE
El indicador SigHash NONE implica que se firman todos los inputs y ninguno de los outputs, provocando que solo los inputs sean inamovibles. Está representado por la cadena hexadecimal 02.
Este indicador nos permite crear esos "cheques bitcoin" de los que hablé antes. Dado que no se firman los outputs, cualquiera puede modificar el output y ponerlo a una de sus direcciones. Aunque claro, si cualquiera puede hacerlo, incluso si mandas la transacción con tu output en cuestión, alguien que la detectase en la blockchain antes de que se confirmara, podría cambiar ese output y dirigirlo a su dirección (convirtiéndose eventualmente en una guerra de fees).
Para que esto no ocurra, el input firmado con SigHash NONE podría aparecer de la mano de otros inputs con un indicador SigHash que si firma los outputs, como ALL. Al firmar todos los inputs, estás ligando inevitablemente tu input NONE al otro input ALL; y a su vez, como el otro input ALL firma toda la transacción, ahora los outputs son inamovibles.
Sería algo como "dime que input vas a gastar, yo te aporto un input, y tu te lo gastas donde quieras". Equivalente a un regalo bitcoin o como decíamos, un cheque bitcoin (aunque nominativo, no al portador).
SINGLE
El indicador SigHash SINGLE implica que se firman todos los inputs y solo el output que tiene el mismo índice que el input, provocando que los inputs y un solo output sean inamovibles. Está representado por la cadena hexadecimal 03.
Sería como "Mientras una parte de los fondos vayan a este output, no me importa dónde vaya el resto".
Modificador ANYONECANPAY
Para crear los siguientes tres tipos de SigHash, se añade el modificador ANYONECANPAY a los tres tipos que veíamos previamente. Este modificador permite que se puedan añadir inputs a los ya existentes, dejando la puerta abierta a individuos que quieran completar la transacción aportando fondos. De ahí el nombre ANYONECANPAY (Cualquiera puede pagar).
Cuando el indicador SigHash incluye ANYONECANPAY, la firma se aplica únicamente al input específico del firmante, excluyendo los demás (si los hubiera). Esto se representa con la cadena hexadecimal 80.
Donde antes solo podíamos modificar los outputs que se firman (en NONE y SINGLE), ahora con ANYONECANPAY también se podrán modificar los inputs, añadiendo nuevos a los ya existentes.
ALL | ANYONECANPAY
El indicador SigHash ALL|ANYONECANPAY implica que se firma solo un input y todos los outputs, provocando que esas partes sean inamovibles y que se puedan añadir otros inputs siempre y cuando esos inputs también firmen los mismos outputs. Está representado por la cadena hexadecimal 81 (01 ALL & 80 ANYONECANPAY = 81 ALL|ANYONECANPAY).
Este indicador permite transacciones de "crowdfunding". Imagina que un autor necesita 1 BTC para imprimir su libro:
    El autor firma una transacción con la dirección donde recibirá los fondos, usando SigHash ALL|ANYONECANPAY. Este output y su input quedan inamovibles.
    Los contribuyentes añaden sus propios inputs a la transacción, firmándolos con el mismo indicador. Así, cada participante puede colaborar sin modificar los inputs y el output ya existentes, permitiendo a su vez que más contribuyentes se puedan seguir uniendo.
    Cuando los inputs acumulados suman 1 BTC, el último contribuyente la emite a la red.
"A" sería el autor del libro, mientras que "B" y "C" serían los contribuyentes.
Sería como "firmo un output para el que no tengo suficientes fondos y espero a que otros vayan sumando sus inputs hasta poder realizar la transacción".
NONE | ANYONECANPAY
El indicador SigHash NONE|ANYONECANPAY implica que se firma solo un input y ninguno de los outputs, provocando que solo el input firmado sea inamovible. Está representado por la cadena hexadecimal 82 (02 NONE & 80 ANYONECANPAY = 82 NONE|ANYONECANPAY).
Este indicador provoca que sea un input totalmente suelto, ligado a nada. Cualquier persona puede cogerlo y añadirlo a su transacción. En este caso si sería como un cheque al portador o como un regalo, con el problema de que no puedes estar seguro que finalmente los fondos vayan a la dirección que desees (si has llegado a definir una).
Sería como "firmo este input para que se pueda gastar, pero me es indiferente a donde vayan los fondos o si se une con otros inputs".
SINGLE | ANYONECANPAY
El indicador SigHash SINGLE|ANYONECANPAY implica que se firma solo un input y solo el output que tiene el mismo índice que el input, provocando que solo esas partes sean inamovibles. Está representado por la cadena hexadecimal 83 (03 SINGLE & 80 ANYONECANPAY = 83 SINGLE|ANYONECANPAY).
Aunque su utilidad es más limitada, es uno de los más interesantes de describir. Al firmar solo un input y solo el output con el mismo índice, se crea la posibilidad de que se puedan añadir otros inputs y otros outputs sin apenas límite.
Permite crear transacciones modulares, que se pueden acoplar y desacoplar de otras con relativa facilidad (existen limitaciones que describo en el apartado EXTRA: Detalles Técnicos en la Serialización de la Proto-transacción). Tanto es así que una vez enviada a la red, una de estas transacciones modulares podría ser incorporada en otra transacción distinta, no necesariamente vinculada a la original. Esto permitiría aprovechar cualquier fondo excedente de Satoshis que no se haya asignado a tu output.
Incluso podríamos pensar que se puede emplear para hacer CoinJoins: cada cual que firme su input y su output y después acoplamos todas esas transacciones en una sola. Aunque a primera vista podría ser factible hacer un CoinJoin de esta forma, realmente no estaríamos consiguiendo privacidad: dado que firmamos el output del mismo índice que nuestro input (por ejemplo, si nuestro input es el sexto, nuestro output firmado será el sexto), no se estaría rompiendo el nexo, pudiéndose determinar a quién le pertenece cada output.
Para que un CoinJoin logre el nivel de privacidad deseado, es crucial coordinar a los participantes antes de que la transacción sea firmada, no después. Esto requiere un coordinador, cuya función es recopilar los inputs (direcciones de origen de los fondos) y los outputs (direcciones de destino) de los participantes, y construir la transacción completa.
El rol del coordinador es también esencial para maximizar el conjunto de anonimato (Anon Set), dificultando la trazabilidad e incorporando suficientes participantes legítimos para diluir posibles intentos de ataque por parte de participantes maliciosos.
Una vez que la transacción está preparada, todos los participantes la firman utilizando el indicador SigHash ALL. Esto asegura que no exista una relación directa a nivel de firma entre los inputs y los outputs individuales, a diferencia de lo que ocurre con indicadores como SINGLE|ANYONECANPAY.
Este enfoque representa un cambio fundamental: se pasa de una coordinación posterior a la firma (lo que posibilitan los indicadores SigHash) a una coordinación previa a esta.
Conclusión
Los indicadores SigHash son una de las gemas ocultas de Bitcoin, menos conocidas pero extremadamente interesantes.
Habilitan nuevos casos de uso en la red de Bitcoin y sobre todo, facilitan que distintos individuos participen en la construcción de una transacción sin la necesidad de confianza entre ellos; manteniéndose ese espíritu trust-less.
A continuación te dejo cuatro apartados extra con los que indagar más en el tema.
EXTRA: La "Chapucilla" de SigHash SINGLE
Cuando vas aprendiendo sobre Bitcoin y te das cuenta de la cantidad de elementos que lo conforman y la manera casi mágica en la que estos se relacionan, es difícil imaginarse que Satoshi hizo alguna que otra "chapucilla".
Pues una de esas "chapucillas" está presente a la hora de firmar transacciones con el indicador SigHash SINGLE (y por extensión también en SINGLE|ANYONECANPAY). Si recuerdas, este indicador firma el output con el mismo índice que el input que está firmando.
¿Pero, y si hay más inputs que outputs? ¿Ahora qué hacemos?
En efecto, si tenemos una transacción con dos inputs y un output, y firmamos el segundo input con el indicador SINGLE, no habrá un output con el mismo índice que firmar.
Bueno, pues la maravillosa idea de Satoshi fue que en vez de firmar una proto-transacción, se firmara directamente el número 1 [Nota 2]. Sí, como lo oyes. El número 1. Y por si fuera poco, la transacción es válida, puesto que existe un bug en el código de bitcoin que hace que no se clasifique como error.
Esto implica que, al firmar una transacción con esta "chapucilla", la transacción sea completamente válida. Sin embargo, si el input que utilizaste recibe nuevos fondos, un atacante podría explotar este mismo bug en otra transacción, extrayendo esos nuevos fondos. Esto es posible porque se firma el valor 1 en lugar de un elemento vinculado directamente a la transacción. Como resultado, la firma digital puede reutilizarse, permitiendo que el bug se pueda aprovechar nuevamente para desviar los fondos recibidos.
Este bug está presente desde la primera versión de Bitcoin, la 0.1.0 y fue notificado en 2012. No se ha parcheado principalmente porque tanto SINGLE como SINGLE|ANYONECANPAY tienen limitados casos de uso, porque provocaría un hardfork y porque se presume que, quien hace uso de este tipo de SigHash, conoce la vulnerabilidad. Además, Taproot y wallets como el de Bitcoin Core (Bitcoin Qt), impiden que firmes inputs con SINGLE que su índice supere el número de outputs.
EXTRA: La mejor manera de aprender: probando.
Ha llegado la hora de lo bueno.
Con el siguiente código que he preparado, puedes verificar cualquier transacción sea cual sea su SigHash. Además, podrás pedirle al código que te genere la proto-transacción para cada uno de los inputs, así como pedirle que extraiga otros elementos: Claves Públicas, tipo de SigHash, valores de la firma... Eso sí, los inputs deben ser P2PKH o P2PK, esto es, direcciones que empiezan por 1 (Legacy) o por 02, 03 o 04.
Por otro lado, si te planteas donde se encuentra el indicador SigHash dentro de la transacción, lo podrás encontrar al final de los valores r y s de la firma. Si tienes un nodo propio, puedes pasarle el hexadecimal de la transacción que desees y en el campo "asm" de la firma pondrá de manera textual qué indicador SigHash tiene el input en cuestión; corriendo el siguiente comando:
bitcoin-cli decoderawtransaction <transacción en hexadecimal>
EXTRA: Detalles Técnicos en la Serialización de la Proto-transacción
A la hora de serializar la proto-transacción, existen una serie de detalles técnicos que merece la pena recalcar y que se puedan observar en la función getPreimage del código.
    ALL, NONE y SINGLE: Todos los inputs que se firmen que no sean el propio que se está firmando, tendrán su Locking Script a 0x00
    SIGHASH NONE : El valor nSequence del resto de inputs a 0.
    SIGHASH SINGLE : El valor nSequence del resto de inputs a 0. Si hay outputs antes que el output que estás firmando (el que tiene el mismo índice que el input a firmar), hay que contemplar esos outputs como dummies (poner la cantidad de Satoshis al máximo y el Locking Script del output a 0x00)
    SIGHASH SINGLE|ANYONECANPAY : Si hay outputs antes que el output que tu estás firmando, hay que contemplar esos outputs como dummies (poner la cantidad de Satoshis al máximo y el Locking Script del output a 0x00)
    SIGHASH SINGLE y SINGLE|ANYONECANPAY: Si el índice del input a firmar supera el número de outputs, se firmará el valor 1 [Nota 2].
EXTRA: Historia del Artículo
Cuando publiqué mi anterior artículo, el tema de los tipos de indicadores SigHash y sus usos era algo que tenía pendiente.
Como de costumbre, uno piensa que ese "detallito" que te falta es algo sin mucha importancia y que un par de ratos te lo has ventilado. Pero claro, Bitcoin lo vuelve hacer y te mete en una de sus muchas madrigueras. Lo que era un par de ratos se convierte en mucho ratos.
Y es así como nace este artículo.
El primer reto fue conseguir verificar una transacción con más de un input. En el anterior artículo dejaba claro en el apartado "EXTRA: Verificando transacciones" que el código que proponía solo funcionaba con un solo input. Eso se debía a mi desconocimiento en aquel entonces de cómo verificar para varios inputs, más precisamente, como se construye la proto-transacción para uno de los inputs de una transacción con múltiples inputs.
Tras preguntar y sobre todo probar (serializando proto-transacciones a mano), conseguí obtener la forma correcta de la proto-transacción para la que se verificaba la transacción con múltiples inputs. Ojalá haber leído este artículo antes, el tiempo que me hubiera ahorrado :'D
A partir de ese momento continué investigando sobre los tipos de indicadores SigHash y fui ampliando ese código para que fuera capaz de verificar todos los tipos. El problema surge de nuevo con SigHash SINGLE: no era capaz de verificar una transacción que tuviera esas características, por los detalles que comento en el anterior apartado.
Incluso monté un nodo bitcoin con el que buscar transacciones de este tipo para intentar hacer debugging a esas transacciones.
Finalmente, y gracias a dos almas caritativas del foro de BitcoinTalk, conseguí comprender que fallaba y el código terminó funcionando.
Y una vez todo estuvo en orden, redacté el artículo. SalvaZ a 5 de Enero de 2025.
Notas:
Nota 1:
Siempre y cuando el input sea estándar: es decir, emplee firmas digitales para ser desbloqueado (P2PK, P2MS, P2PKH, P2SH-P2PKH, P2SH-P2WPKH, P2WPKH). En casos como P2SH donde el redeem script no necesite de firmas digitales para ser desbloqueado, el SigHash flag no estará presente.
Nota 2:
El número 1 de 256bits en hexadecimal, con byte order "Little Endian": 0100000000000000000000000000000000000000000000000000000000000000
En decimal es el número: 452312848583266388373324160190187140051835877600158453279131187530910662656
Agradecimientos:
Agradecer a b10c@PatrimonioBTC por su aportación de una base de datos con el recuento de la cantidad de apariciones de cada SigHash y la revisión del artículo, respectivamente.