¿Cómo redimir un Tx básico?

Dado un Tx estándar sin procesar ( wiki ):

01000000

01
26c07ece0bce7cda0ccd14d99e205f118cde27e83dd75da7b141fe487b5528fb
00000000
8b
48304502202b7e37831273d74c8b5b1956c23e79acd660635a8d1063d413c50b218eb6bc8a022100a10a3a7b5aaa0f07827207daf81f718f51eeac96695cf1ef9f2020f21a0de02f01410452684bce6797a0a50d028e9632be0c2a7e5031b710972c2a3285520fb29fcd4ecfb5fc2bf86a1e7578e4f8a305eeb341d1c6fc0173e5837e2d3c7b178aade078
ffffffff

02

b06c191e01000000
19
76a9143564a74f9ddb4372301c49154605573d7d1a88fe88ac

00e1f50500000000
19
76a914010966776006953d5567439e5e39f86a0d273bee88ac
00000000

y una clave privada:

18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725

¿Cómo se construye una nueva transacción para canjear la moneda de la segunda salida? He estado tratando de resolver esto durante mucho tiempo usando el diagrama de etotheipi , pero parece que no puedo entender cómo se debe crear una transacción adecuada. Una guía paso a paso sería bienvenida.

in bitcoind, I used the resulting hex to do this signrawtransaction '0100000001eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2010000008a4730440220359f19e3d19dd707053641ff1efd0ca25159d0e0a8cbe13cbdcf38a9608c38ee02207383ebc663253a21101597ed897366e2a748ab29c18a8ca183d02910283e88cc01410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6ffffffff01605af405000000001976a914097072524438d003d23a2f23edb65aae1bb3e46988ac00000000' '[]' '[]' And it returned me 'false' ... I think there is a problem with the code ?
@Daniel, no estoy seguro, pero esto podría deberse a que bitcoind signrawtransaction requiere una transacción de salida anterior real en la cadena de bloques para calcular los fondos. el hash de la transacción dada en esta pregunta es f2b3eb2deb76566e7324307cd47c35eeb88413f971d88519859b1834307ecfec, que blockexplorer.com dice que no existe
@mulllhausen ¿Cómo se hizo esto si el txid es incorrecto?

Respuestas (3)

En esta respuesta, seguiré los pasos necesarios para canjear la segunda salida de la transacción mencionada anteriormente. La respuesta se limitará a canjear una salida del tipo particular presente en esta transacción (una salida que requiere proporcionar una nueva transacción firmada con una clave privada cuya clave pública correspondiente se convierte en hash en el script de la salida en cuestión), como esta respuesta ya es bastante larga, incluso sin tener en cuenta otros tipos de salida.

Breve resumen: comenzamos construyendo una nueva transacción, con un scriptSig que contiene el scriptPubKey de la salida que queremos canjear. El scriptPubKey de esta transacción contendrá un script que paga a un hash de una clave pública (dirección de Bitcoin). Realizamos un hash SHA256 doble en esta transacción con el tipo de código hash de cuatro bytes SIGHASH_ALL adjunto al final. Firmamos este hash con la clave privada proporcionada anteriormente. Luego, el scriptSig de esta nueva transacción se reemplaza con un script que primero inserta la firma codificada en DER, más el tipo de código hash de un byte SIGHASH_ALL, en la pila, seguido de la clave pública correspondiente de la clave privada codificada en DER.

Descripción paso a paso:

Comenzamos a crear una nueva transacción sin procesar que procesamos y firmamos.

  1. Agregar campo de versión de cuatro bytes:01000000
  2. Varinte de un byte que especifica el número de entradas:01
  3. Hash de 32 bytes de la transacción de la que queremos canjear una salida:eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
  4. Campo de cuatro bytes que indica el índice de salida que queremos canjear de la transacción con el hash anterior (número de salida 2 = índice de salida 1):01000000
  5. Ahora viene el scriptSig. A los efectos de firmar la transacción, esta se rellena temporalmente con el scriptPubKey de la salida que queremos redimir. Primero escribimos una variante de un byte que denota la longitud del scriptSig (0x19 = 25 bytes):19
  6. Luego escribimos el scriptSig temporal que, nuevamente, es el scriptPubKey de la salida que queremos canjear:76a914010966776006953d5567439e5e39f86a0d273bee88ac
  7. Luego escribimos un campo de cuatro bytes que indica la secuencia. Actualmente, esto siempre está configurado en 0xffffffff:ffffffff
  8. Luego viene una variante de un byte que contiene el número de salidas en nuestra nueva transacción. Estableceremos esto en 1 en este ejemplo:01
  9. Luego escribimos un campo de 8 bytes (entero de 64 bits) que contiene la cantidad que queremos canjear de la salida especificada. Estableceré esto en la cantidad total disponible en la salida menos una tarifa de 0.001 BTC (0.999 BTC o 99900000 Satoshis):605af40500000000
  10. Luego comenzamos a escribir el resultado de nuestra transacción. Comenzamos con una variante de un byte que indica la longitud del script de salida (0x19 o 25 bytes):19
  11. Luego, el script de salida real:76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
  12. Luego escribimos el campo "lock time" de cuatro bytes:00000000
  13. Y por último, escribimos un "tipo de código hash" de cuatro bytes (1 en nuestro caso):01000000

    Ahora tenemos los siguientes datos de transacción sin procesar:

    01000000
    01
    eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
    01000000
    19
    76a914010966776006953d5567439e5e39f86a0d273bee88ac
    ffffffff
    01
    605af40500000000
    19
    76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
    00000000
    01000000
    
  14. (etapa de firma) Ahora duplicamos el hash SHA256 de toda esta estructura, lo que produce el hash9302bda273a887cb40c13e02a50b4071a31fd3aae3ae04021b0b843dd61ad18e

  15. A continuación, creamos un par de claves pública/privada a partir de la clave privada proporcionada. Firmamos el hash del paso 14 con la clave privada, que produce la siguiente firma codificada en DER (esta firma será diferente en su caso): 30460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc06A esta firma le agregamos el tipo de código hash de un byte: 01. La clave pública es:0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
  16. Construimos el scriptSig final concatenando:

    • Script OPCODE de un byte que contiene la longitud de la firma codificada en DER más 1 (la longitud del tipo de código hash de un byte)
    • La firma codificada en DER real más el tipo de código hash de un byte
    • Script OPCODE de un byte que contiene la longitud de la clave pública
    • La clave pública real
  17. Luego reemplazamos el campo de longitud variable de un byte del paso 5 con la longitud de los datos del paso 16. La longitud es de 140 bytes, o 0x8C bytes:8c

  18. Y reemplazamos el scriptSig temporal del Paso 6 con la estructura de datos construida en el paso 16. Esto se convierte en:4930460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc0601410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
  19. Terminamos eliminando el tipo de código hash de cuatro bytes que agregamos en el paso 13 y terminamos con la siguiente secuencia de bytes, que es la transacción final:

    01000000
    01
    eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
    01000000
    8c
    4930460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc0601410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
    ffffffff
    01
    605af40500000000
    19
    76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
    00000000
    

Código de ejemplo de Python:

He creado un script Python de ejemplo que hace todo lo anterior. Es intencionalmente lo más detallado posible y con muchos comentarios, con la menor cantidad de funciones posible, para parecerse a la guía paso a paso anterior. El número de líneas de código se puede reducir fácilmente a la mitad, pero elijo publicarlo en este formato detallado porque considero que es el más fácil de seguir (es decir, sin 'saltar' hacia adelante y hacia atrás a través de las funciones). El guión contiene 76 líneas no vacías y sin comentarios. El script depende de bitcointools (para serializar y deserializar transacciones y codificar/descodificador base58) y ecdsa_ssl.pyde mi fork of joric's brutus repository (para construir pares de claves EC públicas/privadas y firma ECDSA). La forma más fácil de ejecutar el script es clonar bitcointools en una carpeta y poner ecdsa_ssl.py de la URL anterior en la misma carpeta junto con este script y ejecutar el script desde allí. Deberá reemplazar la dirección en la SEND_TO_ADDRESSvariable en este script con la dirección a la que desea que se envíen las monedas, a menos que se sienta generoso :).

#herramientasbitcoin
de deserializar importación parse_Transaction, opcodes
de BCDataStream importar BCDataStream
desde base58 importar bc_address_to_hash_160, b58decode, public_key_to_bc_address, hash_160_to_bc_address

importar ecdsa_ssl

importar Crypto.Hash.SHA256 como sha256
importar Crypto.Random

#transacción, de la que queremos canjear una salida
HEX_TRANSACTION="010000000126c07ece0bce7cda0ccd14d99e205f118cde27e83dd75da7b141fe487b5528fb000000008b48304502202b7e37831273d74c8b5b1956c23e79acd660635a8d1063d413c50b218eb6bc8a022100a10a3a7b5aaa0f07827207daf81f718f51eeac96695cf1ef9f2020f21a0de02f01410452684bce6797a0a50d028e9632be0c2a7e5031b710972c2a3285520fb29fcd4ecfb5fc2bf86a1e7578e4f8a305eeb341d1c6fc0173e5837e2d3c7b178aade078ffffffff02b06c191e010000001976a9143564a74f9ddb4372301c49154605573d7d1a88fe88ac00e1f505000000001976a914010966776006953d5567439e5e39f86a0d273bee88ac00000000"
#salida a redimir. debe existir en HEX_TRANSACTION
SALIDA_ÍNDICE=1
#dirección a la que queremos enviar las monedas canjeadas.
#REEMPLAZAR CON TU PROPIA DIRECCIÓN, a menos que te sientas generoso
SEND_TO_ADDRESS="1L4xtXCdJNiYnyqE6UsB8KSJvqEuXjz6aK"
#tarifa que queremos pagar (en BTC)
TX_FEE=0.001
#constante que define el número de Satoshis por BTC
MONEDA=100000000
#constant utilizado para determinar qué parte de la transacción se codifica.
SIGHASH_ALL=1
#clave privada cuya clave pública se basa en el hash contenido en scriptPubKey del número de salida *OUTPUT_INDEX* en la transacción descrita en HEX_TRANSACTION
CLAVE_PRIVADA=0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725

def dsha256(datos):
   devuelve sha256.new(sha256.new(datos).digest()).digest()

tx_data=HEX_TRANSACTION.decode('hex_codec')
tx_hash=dsha256(tx_datos)

#aquí usamos bitcointools para analizar una transacción. esto da fácil acceso a los distintos campos de la transacción de la que queremos canjear una salida
corriente = BCDataStream()
stream.write(tx_data)
tx_info = parse_Transaction(flujo)

si len(tx_info['txOut']) <(OUTPUT_INDEX+1):
   aumentar RuntimeError, "solo hay %d salida(s) en la transacción que está intentando canjear. Quiere canjear el índice de salida %d" % (len(tx_info['txOut']), OUTPUT_INDEX)

#este diccionario se utiliza para almacenar los valores de los distintos campos de transacción
# esto es útil porque necesitamos construir una transacción para codificar y firmar
# y otra que será la transacción final
campos_tx = {}

##aquí comenzamos a crear la transacción que codificamos y firmamos
sign_tx = BCDataStream()
##primero escribimos el número de versión, que es 1
tx_fields['versión'] = 1
sign_tx.write_int32(tx_fields['versión'])
##entonces escribimos el número de entradas de transacciones, que es uno
campos_tx['num_txin'] = 1
sign_tx.write_compact_size(tx_fields['num_txin'])

##luego escribimos los datos reales de la transacción
#'prevout_hash'
tx_fields['prevout_hash'] = tx_hash
sign_tx.write(tx_fields['prevout_hash']) #hash de la transacción de la que queremos canjear una salida
#'prevout_n'
tx_fields['output_index'] = ÍNDICE_SALIDA
sign_tx.write_uint32(tx_fields['output_index']) #¿qué salida de la transacción con tx id 'prevout_hash' queremos canjear?

##luego viene la parte de la entrada de la transacción. aquí colocamos el script de la *salida* que queremos redimir
tx_fields['scriptSigHash'] = tx_info['txOut'][OUTPUT_INDEX]['scriptPubKey']
#primero escribe el tamaño
sign_tx.write_compact_size(len(tx_fields['scriptSigHash']))
#luego los datos
sign_tx.write(tx_fields['scriptSigHash'])

#'secuencia'
tx_fields['secuencia'] = 0xffffffff
sign_tx.write_uint32(tx_fields['secuencia'])

##entonces escribimos el número de salidas de transacciones. solo usaremos una sola salida en este ejemplo
tx_fields['num_txout'] = 1
sign_tx.write_compact_size(tx_fields['num_txout'])
##luego escribimos los datos de salida de la transacción real
#canjearemos todo desde la salida original menos TX_FEE
tx_fields['value'] = tx_info['txOut'][OUTPUT_INDEX]['value']-(TX_FEE*COIN)
sign_tx.write_int64(tx_fields['valor'])
##aquí es donde va nuestro scriptPubKey (un script que paga a una dirección)
#queremos el siguiente script:
#"OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG"
address_hash = bc_address_to_hash_160(SEND_TO_ADDRESS)
#chr(20) es la longitud de address_hash (20 bytes o 160 bits)
scriptPubKey = chr(códigos de operación.OP_DUP) + chr(códigos de operación.OP_HASH160) + \
   chr(20) + dirección_hash + chr(códigos de operación.OP_EQUALVERIFY) + chr(códigos de operación.OP_CHECKSIG)
#primero escriba la longitud de este grupo de datos
tx_fields['scriptPubKey'] = scriptPubKey
sign_tx.write_compact_size(len(tx_fields['scriptPubKey']))
#luego los datos
sign_tx.write(tx_fields['scriptPubKey'])

#escribir tiempo de bloqueo (0)
tx_fields['tiempo de bloqueo'] = 0
sign_tx.write_uint32(tx_fields['tiempo de bloqueo'])
#y tipo de código hash (1)
tx_fields['hash_type'] = SIGHASH_ALL
sign_tx.write_int32(tx_fields['tipo_hash'])

#luego obtenemos el hash de la transacción sin firma (el hash que firmamos usando nuestra clave privada)
hash_scriptless = dsha256(sign_tx.input)

##ahora comenzamos con las cosas de ECDSA.
## creamos una clave privada a partir de los datos de clave privada proporcionados y firmamos hash_scriptless con ella
## también verificamos que la clave pública correspondiente de la clave privada realmente pueda canjear la salida especificada

k = ecdsa_ssl.KEY()
k.generate(('%064x' % PRIVATE_KEY).decode('hex'))

#aquí recuperamos los datos de la clave pública generados a partir de la clave privada suministrada
pubkey_data = k.get_pubkey()
#luego creamos una firma sobre el hash de la transacción sin firma
sig_data=k.sign(hash_scriptless)
#Se agrega un "tipo hash" de un byte al final de la firma (https://en.bitcoin.it/wiki/OP_CHECKSIG)
sig_data = sig_data + chr(SIGHASH_ALL)

#comprobemos que la clave privada provista realmente pueda canjear la salida en cuestión
if (bc_address_to_hash_160(public_key_to_bc_address(pubkey_data)) != tx_info['txOut'][OUTPUT_INDEX]['scriptPubKey'][3:-2]):
   bytes = b58decode(SEND_TO_ADDRESS, 25)
   raise RuntimeError, "La clave privada proporcionada no se puede usar para canjear el índice de salida %d\nDebe proporcionar la clave privada para la dirección %s" % \
                           (OUTPUT_INDEX, hash_160_to_bc_address(tx_info['txOut'][OUTPUT_INDEX]['scriptPubKey'][3:-2], bytes[0]))

##ahora comenzamos a crear la transacción final. este es un duplicado de la transacción sin firma,
## con el scriptSig completado con un script que empuja la firma más el tipo de código hash de un byte y la clave pública desde arriba, a la pila

final_tx = BCDataStream()
final_tx.write_int32(tx_fields['versión'])
final_tx.write_compact_size(tx_fields['num_txin'])
final_tx.write(tx_fields['prevout_hash'])
final_tx.write_uint32(tx_fields['output_index'])

##ahora necesitamos escribir el scriptSig real.
## consiste en los valores r y s codificados en DER de la firma, un tipo de código hash de un byte y la clave pública en formato sin comprimir
## también necesitamos anteponer la longitud de estos dos datos (codificados como un solo byte
## que contiene la longitud), antes de cada pieza de datos. esta longitud es un código de operación de script que le dice al
## Intérprete de secuencias de comandos de Bitcoin para insertar los siguientes bytes en la pila

scriptSig = chr(len(sig_data)) + sig_data + chr(len(pubkey_data)) + pubkey_data
#primero escriba la longitud de estos datos
final_tx.write_compact_size(len(scriptSig))
#luego los datos
final_tx.write(scriptSig)

##y luego simplemente escribimos los mismos datos después del scriptSig que está en la transacción sin firma,
# omitiendo el tipo de código hash de cuatro bytes (ya que está codificado en el único byte que sigue a los datos de la firma)

final_tx.write_uint32(tx_fields['secuencia'])
final_tx.write_compact_size(tx_fields['num_txout'])
final_tx.write_int64(tx_fields['valor'])
final_tx.write_compact_size(len(tx_fields['scriptPubKey']))
final_tx.write(tx_fields['scriptPubKey'])
final_tx.write_uint32(tx_fields['tiempo de bloqueo'])

#imprime la transacción final en formato hexadecimal (se puede usar como argumento para la transacción de envío de bitcoind)
imprimir final_tx.input.encode('hex')
¿Estás seguro de que el paso 11 es correcto? Solo muestra la dirección de origen. ¿No debería ser un script como "OP_DUP OP_HASH160 destination_address_in_hash_160_format OP_EQUALVERIFY OP_CHECKSIG"? Donde OP_DUP se reemplaza con "76", etc., como se describe aquí .
Estoy atascado en el paso 15... ¿Alguna idea de lo que podría estar haciendo mal? bitcoin.stackexchange.com/questions/10784/…
Con respecto a mi primer comentario, acabo de notar que la dirección está envuelta en 76 a9 y 88 ac. Así que envía todo de vuelta al origen.
No, no lo devuelve, lo envía a 1runeksijzfVxyrpiyCY2LCBvYsSiFsCm.
@SjorsProvoost Como se mencionó, el script canjea el número de salida OUTPUT_INDEX de la transacción descrita en formato hexadecimal en HEX_TRANSACTION y lo envía a la dirección contenida en SEND_TO_ADDRESS.
Disculpe mi falta de conocimiento, pero ¿es normal que el script dé resultados diferentes cada vez que se ejecuta? No sabía que era necesario generar números aleatorios al crear una transferencia (?)
@Daniel Sí. Las firmas ECDSA requieren un número aleatorio. Suceden cosas malas si no es al azar.
Si desea canjear una transacción con múltiples entradas, ¿cómo genera el hash que debe firmar para cada entrada? ¿Es el mismo hachís para todos?
@runeks > "[...] reemplaza la dirección en la variable SEND_TO_ADDRESS [...] a menos que te sientas generoso :)" Solo para que conste, parece que varias almas se sintieron generosas después de leer tu respuesta. Es asombroso el poder de bitcoin.
¿Qué está k.generate(('%064x' % PRIVATE_KEY).decode('hex'))haciendo? ¿Se supone que debo generar algo a partir de la clave privada y firmar con ESO, o simplemente firmo con la clave privada?
El paso "generar" genera un objeto de clave privada a partir de los datos hexadecimales en PRIVATE_KEY. Entonces, esencialmente, solo está analizando 32 bytes de datos codificados en hexadecimal en una clave privada.
Agregando a la elegante solución dada por runeks: en caso de múltiples entradas (que es bastante común), construimos dos tx diferentes repitiendo desde el paso 1-13. en el primer tx mantenemos la entrada 2 como 00 y en el segundo tx mantenemos la entrada 1 como 00. Luego firmamos ambos tx y los reemplazamos en las dos entradas, generando 2 entradas y salidas firmadas tx.
el punto 3 está en Little Endian
Las herramientas de bitcoin de Gavin han estado muertas desde al menos noviembre de 2017. Runeks tiene una bifurcación indirecta en github.com/runeksvendsen/bitcointools que parece funcionar según sea necesario.
@blockwala, ¿qué quiere decir con '00' en una entrada determinada? ¿Simplemente reemplazando el scriptsig, o reemplazando la estructura de entrada del agujero (hash tx anterior, índice de salida anterior, longitud del script y firma y secuencia del script)?

Esta esencia , parcialmente basada en la respuesta de Runeks, muestra cómo transferir 0.01 bitcoins en Ruby. Obtiene información de la transacción anterior de Blockchain.info, por lo que solo necesita ingresar su clave privada y dirección (esta última es redundante pero útil para la demostración). Agregué muchos comentarios para explicar los pasos involucrados.

Una vez que conozca el ID de la transacción, el vout, el scriptPubKey de esa transacción y la clave privada codificada WIF correspondiente a la clave pública con hash para generar la dirección, puede crear una transacción sin procesar y firmarla sin estar en línea.

He escrito una biblioteca PHP para manejar transacciones sin procesar (entre otras funciones de bitcoin). Así es como canjearía una transacción regular: https://github.com/Bit-Wasp/bitcoin-lib-php Necesita una actualización para admitir firmas P2SH, pero canjeará una transacción regular sin problemas.