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.
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.
01000000
01
eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
01000000
19
76a914010966776006953d5567439e5e39f86a0d273bee88ac
ffffffff
01
605af40500000000
19
76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
00000000
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
(etapa de firma) Ahora duplicamos el hash SHA256 de toda esta estructura, lo que produce el hash9302bda273a887cb40c13e02a50b4071a31fd3aae3ae04021b0b843dd61ad18e
30460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc06
A esta firma le agregamos el tipo de código hash de un byte: 01
. La clave pública es:0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
Construimos el scriptSig final concatenando:
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
4930460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc0601410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
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_ADDRESS
variable 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')
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?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.
usuario7295
mullhausen
f2b3eb2deb76566e7324307cd47c35eeb88413f971d88519859b1834307ecfec
, que blockexplorer.com dice que no existemago de ozzie