Analógico web3.eth.accounts.sign (de web3js) en web3py

Hay una función web3.eth.accounts.sign en la implementación de web3js. Pero no puedo encontrar el equivalente exacto en web3py. No veo esto. Encontré algo similar a esto: la función signHash (enlace https://web3py.readthedocs.io/en/stable/web3.eth.account.html#sign-a-message ). Esta documentación dice que el mismo mecanismo hash, pero como resultado, la ejecución no devuelve lo mismo que en web3js (web3.eth.accounts.sign).

Usé esta función en web3js para obtener la firma (y luego usé la biblioteca ECDSA (en Solidity) para obtener el firmante. En web3js funciona correctamente (pero en web3py - no). Me conecto a la red de prueba (ropsten) a través de Infura.

En web3js (donde msg - lo que está firmado):

let signature = await web3.eth.accounts.sign(msgHash, '0x' + privateKeyUser);
console.log('signature ='+signature.signature);

En web3py (lo intento, pero no funciona):

signature = web3.eth.account.signHash(msgHash, '0x' + privateKeyUser)
print('signature ='+signature.signature.hex())

And for example in variable "msgHash" value: 0x058c3b4c8e5dc4632b5c6b861b2c1861d53e426dc673c907ddf2651942b0f230 And at the output of web3js function (web3.eth.accounts.sign), we get signature: 0xd19241ed816026e846c8511e03a468aa6e038165ecb3c2229eee38b04c64b235408389dfad2b65d776dba236e7de1b63fdb6b2579e70c4ce9ad1b09a35da96521c And this is the correct value. Web3py tiene otro valor.

¿Cuál es el análogo exacto en web3py de cómo funciona esta función web3.eth.accounts.sign en web3js? ¡Gracias!

Sería útil si proporcionara un mensaje de ejemplo que está tratando de firmar, así como los datos sin procesar (codificados en hexadecimal) que web3.jsproduce.
También sería bueno saber si está utilizando un nodo remoto para firmar/manejar claves, o si la clave privada para firmar está disponible en el script para web3.js. (Existe esta distinción en web3.py.)
En general, los métodos de conveniencia para la firma estructurada EIP-712 se agregaron web3.pyen v5 ; los stabledocumentos a los que se vincula son v4. Aclare si necesita específicamente v4, o si v5 también es aceptable.
... Y dado que eres nuevo en SE: estas aclaraciones deberían ir como ediciones a tu pregunta original. (¡Bienvenido! :D)
¡Gracias por la ayuda! Sí, soy nuevo aquí :) Agregué más información.
Ayudame por favor. Agregué más información en mi pregunta inicial. Todavía no sé responder.
Acerca de esto: "En general, los métodos de conveniencia para la firma estructurada EIP-712 se agregaron a web3.py en v5; los documentos estables a los que vincula son v4. Aclare si necesita específicamente v4, o si v5 también es aceptable".
(fin de mi publicación anterior) Probé ambas versiones, la 4 y la 5. Antes de esta, no probé la versión 5 (porque es inestable, según tengo entendido). En la versión 5 veo la oportunidad de trabajar con la función "send_message". Si entiendo correctamente, necesito pasar la instancia de la clase "SignableMessage" (de eth_account.messages). Pero no entiendo hasta el final qué parámetros correctos para la inicialización de esta instancia.
@Alex ¡Bienvenido al intercambio de pilas! Esta pregunta se mejoraría agregando una clave privada (obviamente, una clave "quemadora", ya que se hará pública) y el resultado esperado de la firma, para que sea completamente reproducible. Consulte stackoverflow.com/help/minimal-reproducible-example
@carver, mostré en la descripción inicial de esta pregunta fragmentos de código que describen mi problema. Pero también puedo agregar que luego paso la firma al contrato inteligente y uso la biblioteca ECDSA en Solidity (de OpenZeppelin) - función ECDSA.recover para extraer el firmante.
@carver Agregué en la descripción inicial el resultado esperado de la firma.

Respuestas (3)

Como mencionó en los comentarios que está dispuesto a usar web3.py v5, aquí está el enfoque v5 para firmar un mensaje :

from eth_account import Account, messages

msg_hash_hex = "058c3b4c8e5dc4632b5c6b861b2c1861d53e426dc673c907ddf2651942b0f230"
private_key_hex = "b25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364"

#// This part prepares "version E" messages, using the EIP-191 standard
message = messages.encode_defunct(hexstr=msg_hash_hex)

#// This part signs any EIP-191-valid message
signed_message = Account.sign_message(message, private_key=private_key_hex)
print("signature =", signed_message.signature.hex())

Si desea obtener más información sobre EIP-191, consulte el artículo original sobre EIP-191 . Es breve y legible.

Ese fragmento es equivalente al javascript:

msgHash = "0x058c3b4c8e5dc4632b5c6b861b2c1861d53e426dc673c907ddf2651942b0f230"
privateKeyUser = "b25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364"

// web3.js sign() *only* supports "version E" EIP-191 signing
// so it adds the preamble for you.
let signature = await web3.eth.accounts.sign(msgHash, '0x' + privateKeyUser);
console.log('signature ='+signature.signature);

sign()Tenga en cuenta que el método javascript "firma datos arbitrarios" en lugar de un hash de mensaje. Por lo tanto, aunque parezca que primero lo está codificando, podría firmar el mensaje original en su lugar.

En la versión de python, prepare el mensaje original para firmar con: encode_defunct(text=original_message).

¡Gracias por su respuesta! Pero no he tenido éxito en su consejo:
1) En la versión con web3py, tengo el error: "AttributeError: el objeto 'bytes' no tiene atributo 'encode'" Convertí la variable "msg_hash" en texto. La línea con "encode_defunct" se ha convertido en: "message = encode_defunct(text="0x058c3b4c8e5dc4632b5c6b861b2c1861d53e426dc673c907ddf2651942b0f230")"
Y después de eso comenzó sin errores. y el resultado es: 0x4ef46c84d3b33927369c69da0aa46328a80c88f747ee33e9a597e76e8cf8a723642302bf793c1b80b8979a97416ff64423f0b4cad8a1f0e5e141b3f28a21b9b3
2) En la versión con web3js, no tengo errores. Y resultado: 0xdccea59712c525f67783bc2c6bb5add55447d3f2122f30eccb602c3e629a609f2a13c74c32fcb15799c94d3e00753c7264aee93339b327c2130e2f02ce4abb5c1b, como se puede ver, los resultados no son los mismos.
Como puede ver, los resultados no son los mismos. Para información: tengo la versión de web3py - 5.0.0b2 y la versión de web3js - 1.0.0-beta.55
Ah, perdón por el error tipográfico en encode_defunct. Actualicé la respuesta y obtengo el mismo resultado que obtuviste con web3js.
Comprobado, ¡ahora funciona correctamente! @carver, ¡muchas gracias!

Es difícil saberlo sin ver el contenido de todas las variables, incluidas las claves.

Pero mi intuición es que está mezclando bytes sin procesar y cadenas ASCII hexadecimales: algunas de las variables no son lo que cree que son.

Si es posible, ¿puede editar su pregunta con muchas impresiones y resultados para comparar?

¡Gracias por su respuesta! Necesito escribir una variable ¿dónde está la clave privada? Creo que esto es solo una cosa que falta aquí. Mezcla de datos: en mi opinión, aquí los escribí como cadenas explícitamente). Por favor corrígeme si te entendí mal.

Para cualquiera que haya venido aquí con web3.py < v5:

Use defunct_hash_message en el mensaje antes de usar signHash.

from eth_account.messages import defunct_hash_message
prepared_message = defunct_hash_message(primitive=msgHash)
signature = web3.eth.account.signHash(prepared_message, '0x' + privateKeyUser)
print('signature =' + signature.signature.hex())