¿Cómo compongo una transacción con OP_RETURN en Python?

Actualmente estoy tratando de descubrir cómo usar Python para construir una transacción con OP_RETURN. Traté de codificar el mensaje yo mismo y no tuve suerte. Encontré una función, OPReturn(), en Internet, pero cuando intento usarla, aparece el error (de la API de transmisión de Blockchain.info):Exception: None Standard Script Output OP_RETURN 594f4c4f53574147

Código:

# coding: utf-8

from bitcoin import *
import binascii
from test import *


priv = sha256('brain wallet')

pub = privtopub(priv)

addr = pubtoaddr(pub)

inputs = unspent(addr)

message = "YOLOSWAG"
FullLen = format(len(message)+2,'x').rjust(2,'0')
MessageLen = format(len(message),'x').rjust(2,'0')
ID = binascii.hexlify(str(message))
snd = "6a"+MessageLen+ID

outputs = [{'value': 50000, 'address': addr}, {'value': 0, 'script': snd}]

fee = 10000

tx = mksend(inputs, outputs, addr, fee)

dt = deserialize(tx)
ins = dt['ins']

#print addr
#print ins


for ind, elm in enumerate(ins):
    print elm
    tx = sign(tx, ind, priv)

#print tx



print(pushtx(tx))
Tenga en cuenta que este código solo funcionará en mensajes de 75 bytes o menos.
@NickODell, ¿no son 40 bytes? (EDITAR: ¿te refieres a 75 bytes en total que acabo de darme cuenta?)
¿Qué versión de Python? Estoy bastante seguro de que está intentando hacer esto en Python 3.x, donde el campo struct "q/Q" ya no está disponible. Pybitcointools requerirá Python 2.7 (de hecho, prácticamente todas las cosas de Bitcoin Python son la versión 2.x debido a la forma en que maneja las cadenas como bytes sin binascii y demás.

Respuestas (3)

He bifurcado la biblioteca pybitcointools para devolver una OP_RETURNcadena hexadecimal con el formato correcto, o insertarla OP_RETURNen una transacción hexadecimal sin procesar.

Mi tenedor se puede encontrar aquí . El código es el siguiente:

from bitcoin.pyspecials import safe_hexlify, from_string_to_bytes, from_int_to_byte, from_string_to_bytes

def mk_opreturn(msg, rawtx=None, json=0):
    def op_push(data):
        import struct
        if len(data) < 0x4c:
            return from_int_to_byte(len(data)) + from_string_to_bytes(data)
        elif len(data) < 0xff:
            return from_int_to_byte(76) + struct.pack('<B', len(data)) + from_string_to_bytes(data)
        elif len(data) < 0xffff:
            return from_int_to_byte(77) + struct.pack('<H', len(data)) + from_string_to_bytes(data)
        elif len(data) < 0xffffffff:
            return from_int_to_byte(78) + struct.pack('<I', len(data)) + from_string_to_bytes(data)
        else: raise Exception("Input data error. Rawtx must be hex chars" \
                            + "0xffffffff > len(data) > 0")

    orhex = safe_hexlify(b'\x6a' + op_push(msg))
    orjson = {'script' : orhex, 'value' : 0}
    if rawtx is not None:
        try:
            txo = deserialize(rawtx)
            if not 'outs' in txo.keys(): raise Exception("OP_Return cannot be the sole output!")
            txo['outs'].append(orjson)
            newrawtx = serialize(txo)
            return newrawtx
        except:
            raise Exception("Raw Tx Error!")
    return orhex if not json else orjson

Tenga en cuenta que el nombre del módulo se ha cambiado a btc(desde bitcoin) en mi bifurcación.

Para ejecutar esto, usará os.chdir("c:/python/pybitcointools")(o cualquier directorio en el que se haya descargado. Luego from bitcoin import *. Ahora, usemos msg = 'The enemy of my enemy is my friend'y rawtx = "01000000016e3cd2b24fcf49259db29888ec5fe6521070041cb8c7bb2017537046f9e00f2b0000000000ffffffff0168d61100000000001976a91469bbbb16301e40b9fb67130e1aa53a2281d60af088ac00000000".

mk_opreturn(msg, rawtx)devoluciones:

01000000016e3cd2b24fcf49259db29888ec5fe6521070041cb8c7bb2017537046f9e00f2b0000000000ffffffff0268d61100000000001976a91469bbbb16301e40b9fb67130e1aa53a2281d60af088ac0000000000000000246a2254686520656e656d79206f66206d7920656e656d79206973206d7920667269656e6400000000

Este es el Tx sin procesar con el OP_RETURNcorrectamente insertado. Ejecute la función sin el rawtxparámetro y devuelve la cadena.6a2254686520656e656d79206f66206d7920656e656d79206973206d7920667269656e64

¿Querías responder esto dos veces? PD. Te envié un PR en github. :)
Si y no :). Sentí que necesitaba una respuesta separada porque estoy tratando de responder la pregunta "cómo usar fácilmente OP_RETURN con Python usando código". Esta respuesta es más autónoma, creo. Buena captura en el PR BTW

El script es simplemente (por así decirlo) tomando una transacción sin procesar y empalmando un número hexadecimal que representa una salida Tx adicional. Entonces está buscando ffffffff ( secuencia ) y agregando 6a hex encoding of your msg(hasta 20 bytes)

El mensaje necesita convertirse a hexadecimal (lo que hace el código).

El código funciona bien para mí, aunque todavía tengo que intentar transmitir. Basado en su error citado

Recibo el error (de la API de transmisión de Blockchain.info): Excepción: Ninguna Salida de script estándar OP_RETURN 594f4c4f53574147

Tendría que decir que BCI está reaccionando a la MessageLenparte del código. Debe haber un OP_PUSHDATA1( 0x4c) entre OP_RETURN( 0x6a) y la longitud del mensaje de 1 byte, 0x08.

Probar:6a4c08594f4c4f53574147

Alternativamente, intente con otro servicio para enviar el Tx sin procesar, por ejemplo:

¿Mi línea ID = binascii.hexlify(str(message))ya no se ocupa de eso?
@rsmoz déjame echarte un vistazo. ¿Puedes confirmar la URL del script y la versión de Python en la que estás?
2.7. Estoy a punto de actualizar el código con algunos cambios que he hecho.
Agregué el código push, no funcionó. Probé el envío de tx de Bitpay, me dijo que mi transacción era polvo, lo que recordé significaba que la transacción era demasiado pequeña. ¡Lo hizo más grande, luego usó el envío de Bitpay y funcionó! blockchain.info/tx/…
@rsmoz cosas buenas! Me interesa saber si fue el límite de polvo de 550 Satoshi o el código push op lo que lo hizo. Editaré mi respuesta para eliminar los comentarios inútiles de binascii que hice
Hmm, todavía tengo algunos problemas. Ahora que tengo muchas entradas con las que lidiar, la transacción no se está ensamblando correctamente.
Quería agregar una aclaración: al ensamblar un OP_RETURN Txn de 40 bytes, el código es: 2a6a28 40 byte data, donde 2adice empujar los siguientes 42 bytes, 6aes OP_RETURN y 28dice empujar los 40 bytes. Este es el formato requerido para Bitcoincore.

Es posible que encuentre útil nuestra biblioteca python-OP_RETURN, ya sea para usarla de forma inmediata o para mirar dentro para ver cómo estamos creando transacciones OP_RETURN.

https://github.com/coinspark/python-OP_RETURN

También tiene una característica interesante para almacenar datos de tamaño arbitrario en la cadena de bloques mediante múltiples transacciones encadenadas con OP_RETURN y para recuperar esos datos mediante un único número de referencia de 12 dígitos.

¿A qué corresponde el número de referencia?
Tiene el formato: [altura estimada]-[txid parcial] [altura estimada] es el bloque donde puede aparecer la primera transacción. Al almacenar, establecemos [altura estimada] en 1+ (altura actual). Al recuperar, probamos ese bloque, luego algunos más tarde y algunos antes en caso de reorganizaciones. [txid parcial] contiene 2 bytes adyacentes del txid: position=2*([txid parcial] div 65536) ([txid parcial] mod 256) es el byte en la posición ([txid parcial] mod 65536) div 256) es el byte en (posición + 1) No puede usar un txid para la referencia ya que la mayoría de los nodos no indexan por txid.
@Gideon Greenspan: traté de usar su código pero obtuve este error:'OP_RETURN.py", line 456, in OP_RETURN_bitcoin_cmd if not len(port): TypeError: object of type 'int' has no len()'
Establezca OP_RETURN_BITCOIN_PORTusando un número entero en una cadena en lugar de un número entero.