---
# System prepended metadata

title: Maleabilidad de las transacciones "Legacy"
tags: [macos, bitcoin, cli, nodo, node]

---

# Maleabilidad de las transacciones "Legacy"
###### tags: `bitcoin` `cli` `txid` `segwit`

_Cómo demostrar la maleabilidad de las transacciones Legacy_

**Tabla de contenido**
[TOC]

## Autores
EntrePlanctonyBallenas. 
Twitter para correcciones, comentarios o sugerencias: [@entreplanctony1](https://twitter.com/Entreplanctony1)

El presente tutorial fue elaborado para el [Seminario socrático de Mastering Bitcoin](https://libreriadesatoshi.com/) a través de [@libreriadesatoshi](https://twitter.com/libdesatoshi).

En el siguiente enlace puedes encontrar la documentación de referencia:
[Transaction malleability](https://en.bitcoin.it/wiki/Transaction_malleability)

## Maleabilidad de las transacciones
:::warning
**NOTA** esta vulnerabilidad solo aplica a transacciones **legacy**, si decides intentar los pasos aquí descritos debes crear una transacción en una wallet sin descriptors y con direcciones legacy.
:::

Una vez que una transacción es firmada con una llave privada, el contenido de la transacción no puede alterarse ya que cualquier modificación al código hexadecimal de la transacción produciría una firma criptográfica completamente distinta.

Sin embargo, es posible resolver el script de desbloqueo (en el arreglo de **vin**) alterando el formato de la firma misma, sin invalidar la solución de la transacción mediante los OP_CODE. Esto se puede lograr debido a que la firma criptográfica no es parte del hash que se debe firmar y los scripts de desbloqueo son públicos para cualquiera que quiera consultarlos.

Consideremos el siguiente script de bloqueo como ejemplo:

```
2 OP_ADD 4 OP_EQUAL
```

Podemos resolver este script usando el valor `2` como script de desbloqueo, pero hay varias otras formas de expresar el valor correcto de este script, aquí algunos ejemplos:

```
OP_2
OP_PUSH1 0x02
OP_PUSH2 0x0002
OP_PUSH3 0x000002
...
OP_PUSHDATA1 0x0102
OP_PUSHDATA1 0x020002
...
OP_PUSHDATA2 0x000102
OP_PUSHDATA2 0x00020002
...
OP_PUSHDATA4 0x0000000102
OP_PUSHDATA4 0x000000020002
...
```
Al utilizar el valor `2` como script de desbloqueo y calcular el hash de la transacción entera obtendríamos un TXID válido, sin embargo cualquier valor distinto de `2`, por ejemplo `OP_PUSHDATA2 0x000102` producirá un hash completamente distinto y por lo tanto un TXID totalmente distinto al del emisor original de la transacción.

Esto podría representar un peligro bajo ciertas condiciones especificas en las que el emisor esta conectado a un nodo "**deshonesto**" que decida publicar la nueva transacción en lugar de la original.

Veamos un ejemplo con una transacción en **regtest**.

## Mutando una transacción Legacy
Lo primero es decodificar una transacción legacy para encontrar el script de desbloqueo en el arreglo de **vin**, puedes apoyarte en el [Ejemplo para Decodificar una transacción a mano](https://hackmd.io/@entreplanctonyballenas/decode-tx-example) para encontrar el script de desbloqueo de tu transacción.

Como yo tengo una transacción que usamos en [Comprobando una firma de Transacción](https://hackmd.io/@entreplanctonyballenas/unlockscript), voy a usarla para este ejemplo, la información subrayada es el script de desbloqueo.
:::info
020000000179d5220fa628d9fd463fa6fb5cf45df7b49b81467a6eb7fd7d948e3a6ec0f22b00000000==6a47304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d5012103865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45de==ffffffff0200e1f505000000001976a914b47a6f225525f7f4674952ce91ddbc8ef755aee188ac1fb23f71000000001976a914a16ef9a4f940070c8360f7a9bb8853756d5ca2c988ac00000000
:::

La traducción del script seleccionado podemos obtenerla con el comando **decodescript** de `bitcoin-core`. NOTA: Se debe omitir el primer byte ==6a==ya que este representa el tamaño total del script dentro de la transacción.
```json!
$ bitcoin-cli decodescript 47304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d5012103865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45de|jq .asm
"304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d501 03865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45de"
```

### Decodificando a mano el script de desbloqueo
Podemos decodificar el script de **bloqueo**/**desbloqueo** de una manera similar a como decodificamos usando el campo "**scriptsize**" dentro de una transacción. 

Comenzando por el tamaño del script y continuando con cada uno de los bytes del script, cada byte puede ser un valor o una representación de un OP_CODE. Los OP_CODE son valores en hexadecimal que están reservados para interpretar un Operador especifico, definidos en el archivo [script.h](https://github.com/bitcoin/bitcoin/blob/master/src/script/script.h) del código fuente de _Bitcoin Core_.
:::info
**Para el caso específico del script que queremos decodificar:**

==6a== 
Este primer byte representa el "**scriptsize**" y determina el tamaño **total** del script. Son 106 bytes.

==47== 
Tamaño del primer argumento dentro del script es de **71** bytes (hex). 

==304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d501==
Como esta transacción es de tipo **P2PKH** este valor es la firma del contenido de la transacción.

==21== 
Tamaño del siguiente argumento dentro del script que es de **33** bytes (hex).

==03865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45de==
De nuevo, como la transacción es **P2PKH** este valor representa la **llave pública** utilizada para confirmar la firma.
:::
### Modificando el script

Para modificar el script basta con agregar un OP_CODE que cambie el formato en el que presentamos la información del script sin alterar el resultado. Vamos a utilizar el siguiente OP_CODE que se define en el [wiki de bitcoin](https://en.bitcoin.it/wiki/Script).

:::warning
**OP_PUSHDATA2** - Representado por el valor **4d** 
La descripción de este operador indica que los siguientes 2 bytes escritos en **little endian** contienen el numero de bytes que se van a ingresar a la pila del script.
:::
Para agregar **OP_PUSHDATA2** en nuestro script debemos introducir el código ==4d== al inicio de nuestro script.

Si aplicamos este **OP_PUSHDATA2** a la firma del script original tendriamos que cambiar el valor ~~==47==~~ que es la longitud de la firma y representarlo en 2 bytes en formato **little endian**, esto quedaría como ==4700==

Finalmente, hemos agregado un total de 2 bytes nuevos  a la longitud completa de nuestro script por lo que ahora debemos tambien modificar el valor de **scriptsize** al inicio del script ya que este ahora mide 108 bytes, este valor en hexadecimal es de ==6c==.

Nuestro script "**mutado**" quedaría de la siguiente forma:

:::danger
==6c4d4700==304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d5012103865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45de
:::

Si decodificamos el script "**mutado**" con **decodescript** vemos que el resultado del "**asm**" es exactamente el mismo que teníamos con el script original. NOTA: Se debe omitir el primer byte ==6c==ya que este representa el tamaño total del script dentro de la transacción.
```json!
$ bitcoin-cli decodescript 4d4700304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d5012103865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45de |jq .asm
"304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d501 03865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45de"
```
Nuestra transacción "**mutada**" quedaria de la siguiente forma:
:::danger
020000000179d5220fa628d9fd463fa6fb5cf45df7b49b81467a6eb7fd7d948e3a6ec0f22b00000000==6c4d4700304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d5012103865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45de==ffffffff0200e1f505000000001976a914b47a6f225525f7f4674952ce91ddbc8ef755aee188ac1fb23f71000000001976a914a16ef9a4f940070c8360f7a9bb8853756d5ca2c988ac00000000
:::

### Verificando el TXID

Este seria el TXID de la transacción original:
```json!
$ bitcoin-cli decoderawtransaction 020000000179d5220fa628d9fd463fa6fb5cf45df7b49b81467a6eb7fd7d948e3a6ec0f22b000000006a47304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d5012103865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45deffffffff0200e1f505000000001976a914b47a6f225525f7f4674952ce91ddbc8ef755aee188ac1fb23f71000000001976a914a16ef9a4f940070c8360f7a9bb8853756d5ca2c988ac00000000 |jq .txid
"4cdc31f444242a99411e65361dab78f677a68bfa1e4f22f488c684811c30f16d"
```

Y este sería el TXID de la transacción mutada:

```json!
$ bitcoin-cli decoderawtransaction 020000000179d5220fa628d9fd463fa6fb5cf45df7b49b81467a6eb7fd7d948e3a6ec0f22b000000006c4d4700304402207363809dea44ee307f4ce74e6b51b451984625d9212bf8f30bb5862311bfcb8302201312bce412e43550cb4002eab96967abdfc527f161bb47181c86473b0df7e0d5012103865176c3174200de1b60f0dae80f3ed28c920fe78bff02eb0d028d65fcaa45deffffffff0200e1f505000000001976a914b47a6f225525f7f4674952ce91ddbc8ef755aee188ac1fb23f71000000001976a914a16ef9a4f940070c8360f7a9bb8853756d5ca2c988ac00000000 |jq .txid
"4c24cff20d652543346ea9922b5f630c179240649a1e67a1817d6f20ff5611ad"
```
:::success
:tada: Hemos confirmado que se puede modificar el TXID de una transacción Legacy! 
Este problema se resuelve con **segwit** gracias a que los datos del script de desbloqueo no se utilizan para calcular el TXID de una transacción como lo vimos en el [Ejemplo para Decodificar una transacción a mano](https://hackmd.io/@entreplanctonyballenas/decode-tx-example)
:::