matriz de bytes de serialización para datos de transacciones

Actualicé de python 2 a python 3 y parte del código que solía funcionar ahora está roto. Parece que no puedo arreglarlo. ¿Alguna sugerencia?

Trabajó en python 2

# Convert hex string to byte array
hex_string = "0x00012345...."
data_bytearray = bytearray.fromhex(hex_string.replace("0x", ""))

# Then I pass data_bytearray into transactions.Transaction and it works.  
try:
    return {'error': False, 'sign': rlp.encode(transactions.Transaction(nonce, gasPrice_int, gas_int, to, value, data_bytearray).sign(privkey)).encode('hex')}
except Exception as msg:
    return {'error': True, 'message': msg}

El mismo código no funciona en python 3 y tenga en cuenta que el tipo de datos de la matriz de bytes ha cambiado a

<class 'bytearray'>   # python 3 produces this from bytearray.fromhex
<type 'bytearray'>    # python 2 produces this from bytearray.fromhex

Obtuve el siguiente error:

{'error': True, 'message': ObjectSerializationError('Serialization failed because of field data ("Object is not a serializable (<class \'bytearray\'>)")',)}

Intenté serializar la salida usando pickle, pero obtuve otro error:

import pickle
data_bytearray = pickle.dumps(data_bytearray)

{'error': True, 'message': AttributeError("'bytes' object has no attribute 'encode'",)}

¿Qué estoy haciendo mal aquí?

Solución:

try:
    data_hex_with0xRemoved = data_hex.replace("0x", "")
    # If I use bytearray.fromhex, it results in {'error': True, 'message': ObjectSerializationError('Serialization failed because of field data ("Object is not a serializable (<class \'bytearray\'>)")',)}
    # data = bytearray.fromhex('deadbeef')
    # So instead I use bytes.fromhex
    data = bytes.fromhex(data_hex_with0xRemoved)
    unsigned_transaction = transactions.Transaction(nonce, gasPrice_int, gas_int, to, value, data)
    raw_transaction_bytes = rlp.encode(unsigned_transaction.sign(privkey))
    raw_transaction_hex = Web3.toHex(raw_transaction_bytes)
    raw_transaction_hex_0xRemoved = raw_transaction_hex.replace("0x", "")
    return {'error': False, 'sign': raw_transaction_hex_0xRemoved}
except Exception as msg:
    return {'error': True, 'message': msg}
¿Has probado bytesen lugar de bytearray? Además, te puede interesar: data_bytes = Web3.toBytes(hexstr="0x00012345")de web3.py
Sí, tanto bytes como Web3.toBytes me dieron un error similar a pickle.dumps: {'error': True, 'message': AttributeError("El objeto 'bytes' no tiene atributo 'encode'")}.
Esta no es realmente una solución, pero podría llamar a un script py2 que realiza esta operación y devuelve el valor al script py3. Me encontré con este mismo problema y tenía demasiadas batallas más importantes para elegir, así que tomé la solución rápida que me permitió pasar a cosas más importantes. Tengo curiosidad acerca de la manera correcta de hacer esto también.

Respuestas (1)

Solución

try:
    unsigned_transaction = transactions.Transaction(nonce, gasPrice_int, gas_int, to, value, data_bytearray)
    raw_transaction = rlp.encode(unsigned_transaction.sign(privkey))
    return {'error': False, 'sign': Web3.toHex(raw_transaction)}
except Exception as msg:
    return {'error': True, 'message': msg}

El único cambio notable es de raw_transaction.encode('hex')a Web3.toHex(raw_transaction).

Las otras diferencias con la línea original eran simplemente para reducir la cantidad de lógica por línea.


¿Por qué?

ambos bytes y Web3.toBytes me dieron un error similar a pickle.dumps:{'error': True, 'message': AttributeError("'bytes' object has no attribute 'encode'",)}

Este ^ comentario fue el más útil, aunque no explica por qué no obtienes el mismo error con bytearray(creo que deberías). Ver:

In [1]: b'\xaf'.encode('hex')
AttributeError: 'bytes' object has no attribute 'encode'

In [2]: bytearray(b'\xaf').encode('hex')
AttributeError: 'bytearray' object has no attribute 'encode'

Esto realmente no tiene nada que ver con Ethereum, solo con aprender cómo funciona la codificación y el decapado en Python 3. Por ejemplo, esperaría que lo siguiente se comporte exactamente como lo hace el primer ejemplo de código de la pregunta:

try:
    return {'error': False, 'sign': bytearray(b'\xaf').encode('hex')}
except Exception as msg:
    return {'error': True, 'message': msg}

La codificación en Python 2 tenía una API descuidada y permitía a las personas hacer cosas sin sentido, como "codificar" datos binarios (que ya están codificados, siendo binarios). Por lo tanto, se eliminó por completo del tipo encode()Python 3 . bytesSi desea convertir bytesa hexadecimal en Python 3, hay algunas formas de hacerlo:

Mi opción favorita personal

> Web3.toHex(b'\xaf')
'0xaf'

Alternativamente, en Python 3.5+, puede usar:

> b'\xaf'.hex()
'af'
# note that you don't get the "0x" prefix

Otra opción incorporada es binascii

> import binascii
> hex_in_bytes = binascii.hexlify(b'\xaf')
b'af'

# binascii made the nonsensical choice to return the hex value in a `bytes` type
# which you can fix up by decoding to a string

> hex_in_bytes.decode('utf8')
'af'

(Todos estos excepto Web3trabajar con bytearraytambién).

Tenías razón, los problemas con los que me encontré estaban todos relacionados con mis malentendidos sobre python 3 y no sobre ethereum. Gracias por desglosar esto para mí. Publiqué el código final con el que fui arriba. Terminé teniendo que usar bytes.fromhex en lugar de bytearray.fromhex por el motivo mencionado anteriormente.
Lo tengo, también te puede gustar data = Web3.toBytes(hexstr=data_hex).