Cómo decodificar transacciones de Bitcoin manualmente. Ejemplo práctico
Ya usaste Bitcoin Core para ver transacciones, ahora haz una radiografía de como funcionan.
Autor
EntrePlanctonyBallenas.
Twitter para correcciones, comentarios o sugerencias: @entreplanctony1
El presente ejercicio fue elaborado por @niftynei, como parte del curso
Bitcoin Protocol Deep Dive: Transactions
en Udemy. Si consideras valioso este material te sugiero que tomes su curso completo, ya que te será mucha utilidad si quieres adentrarte en el rabbit hole de las transacciones de Bitcoin.
Cheat Sheet de PYTHON
Usamos Python desde la línea de comandos para decodificar más rápido. Aquí una lista de comandos que te harán más fácil el ejercicio.
PARA CONVERTIR UN VALOR DE HEXADECIMAL A ENTERO
int('<CADENA>',16)
PARA CONVERTIR UN VALOR HEXADECIMAL A BIG ENDIAN
bytes.fromhex('<CADENA>')[::-1].hex()
PARA CONVERTIR UN VALOR HEXADECIMAL A BIG ENDIAN Y A ENTERO
int(bytes.fromhex('<CADENA>')[::-1].hex(),16)
PARA MEDIR LA LONGITUD DE UNA CADENA HEXADECIMAL EN BYTES
len ('<CADENA>')/2
Ejemplo para Legacy
Tomamos la siguiente transacción:
txid = ‘f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16’
Valor Hexadecimal de la transacción
0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000
Formulario para identificar los campos de una transacción
🛈 Todos los campos de tamaño fijo están codificados en little endian, mientras que los campos que tienen longitud variable usan Big Endian
Versión de la transacción |
4 bytes |
little endian y convertir a entero |
Numero de inputs |
1 byte |
little endian y convertir a entero |
VINs (vectores de entrada) |
txid |
32 bytes |
little endian |
vout |
4 bytes |
little endian y convertir a entero |
scriptsize |
1 byte |
little endian y convertir a entero |
scriptsig |
variable |
BIG endian |
sequence |
8 bytes |
little endian |
VOUTs (vectores de salida) |
Numero de outputs |
1 byte |
little endian |
amount |
8 bytes |
little endian y convertir a entero |
scriptsize |
1 byte |
little endian y convertir a entero |
scriptsig |
variable |
BIG endian |
LOCKTIME |
4 bytes |
little endian |
Aplicamos el formulario y el Cheat Sheet sobre la transacción
- Versión de la transacción (4 bytes - little endian y convertir a entero)
01000000
Número de inputs (1 byte - little endian y convertir a entero)
01
VINs (vectores de entrada)
txid (32 bytes - little endian)
c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704 - bytes.fromhex('<CADENA>')[::-1].hex()
Resultado: 0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9
- vout (4 bytes - little endian y convertir a entero)
00000000
scriptsize (1 byte - little endian y convertir a entero)
48 - int(bytes.fromhex('<CADENA>')[::-1].hex(),16)
Resultado: 72
- scriptsig (variable - BIG endian)
47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901
sequence (8 bytes - little endian)
ffffffff
VOUTs (vectores de salida)
Número de outputs (1 byte - little endian)
02
amount (8 bytes - little endian y convertir a entero)
00ca9a3b00000000
- int(bytes.fromhex('<CADENA>')[::-1].hex(),16)
Resultado: 10 0000 0000
- scriptsize (1 byte - little endian y convertir a entero)
43
- int(bytes.fromhex('<CADENA>')[::-1].hex(),16)
Resultado: 67
- scriptsig (variable - BIG endian)
4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac
amount (8 bytes - little endian y convertir a entero)
00286bee00000000 - int(bytes.fromhex('<CADENA>')[::-1].hex(),16)
Resultado: 40 0000 0000
- scriptsize (1 byte - little endian y convertir a entero)
43 - int(bytes.fromhex('<CADENA>')[::-1].hex(),16)
Resultado: 67 - scriptsig (variable - BIG endian)
410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac
LOCKTIME (little endian)
00000000
Ejemplo para SegWit
Diferencias en los campos entre Legacy y SegWit
Legacy |
Segwit |
Versión de la transacción |
Versión de la transacción |
Número de inputs |
Segwit marker + Flag |
Txid |
Número de inputs |
Índice de Vout |
Txid |
Scriptsize |
Índice de Vout |
Scriptsig |
Scriptsize |
Sequence |
Sequence |
Número de outputs |
Número de outputs |
Amount |
Amount |
Scriptsize |
Scriptsize |
ScriptLock |
ScriptLock |
LOCKTIME |
Witness Stacks |
|
Scriptsig |
|
LOCKTIME |
Vamos a convertir la misma transacción Legacy a SegWit
Tomaremos el mismo formulario. Solo queremos saber los valores originales, no nos importa convertir a entero o a Big Endian. Hay que modificar la información tomando en cuenta los campos nuevos. Así mismo, los scripts de desbloqueo se mueven al final de los VOUTs.
Versión de la transacción
01000000
segwit marker + flag
00 01
Número de inputs
01
VINs (vectores de entrada)
txid
c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704
vout
00000000
scriptsize
00
sequence
ffffffff
VOUTs (vectores de salida)
Número de outputs
02
amount
00ca9a3b00000000
scriptsize
43
scriptsig
4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac
amount
00286bee00000000
scriptsize
43
scriptsig
410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac
witness stacks
01
scriptsig
47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901
LOCKTIME
00000000
Resultado:
01000000000101c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd37040000000000ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac0147304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d090100000000
Obtener el txid de una transacción en hexadecimal
Comandos para obtener el hash de una transacción
Importar librerías necesarias:
from hashlib import sha256
Obtenemos los bytes del valor hexadecimal de la transacción:
b = bytes.fromhex('<TXHEX>')
Aplicamos 2 veces el sha256 y volvemos a convertir a hexadecimal y BIG endian:
sha256(sha256(b).digest()).digest()[::-1].hex()
TXID Legacy
Es el hash de toda la información de una transacción
from hashlib import sha256
TXHEX = '0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000'
b = bytes.fromhex(TXHEX)
sha256(sha256(b).digest()).digest()[::-1].hex()
Resultado
f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
TXID SegWit
Es el hash de toda la información “Legacy de una transacción”. Hay que remover todos los datos hexadecimales que se usen en SegWit.
from hashlib import sha256
TXHEX = '0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd37040000000000ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000'
b = bytes.fromhex(TXHEX)
sha256(sha256(b).digest()).digest()[::-1].hex()
Resultado
f4999544bfc6aad5c629a8eaa015f7df32d3498569073db7f2cebdf4cf13c1e4
Cómo calcular el peso de una transacción
Legacy
El peso de la transacción, se obtiene de la longitud de la transacción en bytes multiplicada por 4. En terminal correr:
len ('<CADENA>')/2 *4
Resultado: 1100
SegWit
El peso de la transacción se calcula de manera distinta, cada campo tiene un peso distinto de acuerdo a su longitud y de acuerdo a si es un campo Legacy (se multiplica la longitud del campo x 4) o SegWit (se multiplica la longitud del campo x 1). Por lo que hay que aplicar los valores como los encontramos en la transacción:
Versión de la transacción |
4 bytes * 4 = |
16 |
01000000 |
Segwit marker + flag |
1byte + 1byte = 2bytes * 1 = |
2 |
00 01 |
Numero de inputs |
1 byte * 4 = |
4 |
01 |
VINs (vectores de entrada) |
txid |
32 bytes * 4 = |
128 |
c997a5e56e104102fa209c6a852dd90660
a20b2d9c352423edce25857fcd3704 |
vout |
4 bytes * 4 = |
16 |
00000000 |
scriptsize |
1 byte * 4 = |
4 |
00 |
sequence |
4 bytes * 4 = |
16 |
ffffffff |
VOUTs (vectores de salida) |
Numero de outputs |
1 byte * 4 = |
4 |
02 |
amount |
8 bytes * 4 = |
32 |
00ca9a3b00000000 |
scriptsize |
1 byte * 4 = |
4 |
43 |
scriptsig |
67 bytes * 4 = |
268 |
4104ae1a62fe09c5f51b13905f07f06b99a2
f7159b2225f374cd378d71302fa28414e7aa
b37397f554a7df5f142c21c1b7303b8a0626
f1baded5c72a704f7e6cd84cac |
amount |
8 bytes * 4 = |
32 |
00286bee00000000 |
scriptsize |
1 byte * 4 = |
4 |
43 |
scriptsig |
67 bytes * 4 = |
268 |
410411db93e1dcdb8a016b49840f8c53bc1eb
68a382e97b1482ecad7b148a6909a5cb2e0ea
ddfb84ccf9744464f82e160bfa9b8b64f9d4c
03f999b8643f656b412a3ac |
witness stacks |
1 byte * 1 = |
1 |
01 |
scriptsig |
72 bytes * 1 = |
72 |
47304402204e45e16932b8af514961a1d3a1a
25fdf3f4f7732e9d624c6c61548ab5fb8cd41
0220181522ec8eca07de4860a4acdd12909d8
31cc56cbbac4622082221a8768d1d0901 |
LOCKTIME |
4 bytes * 4 = |
16 |
00000000 |
Por lo que el peso de esta transacción SegWit sería: 16 + 2 + 4 + 128 + 16 + 4 + 16 + 4 + 32 + 4 + 268 + 32 + 4 + 268 + 1 + 72 + 16 = 887