¿Cómo puedo simplemente firmar una transacción de Ethereum?

Quiero usar blockcypher para trabajar con la red Ethereum.

En la documentación dan un ejemplo de una herramienta de firma escrita en Go, pero uso Python.

¿Cuál es la forma más sencilla de firmar una transacción?

Intenté usar web3.py , pero encontré algunas dificultades.

Aquí dan el siguiente ejemplo:

transaction = {
       'to': '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55',
       'value': 1000000000,
       'gas': 2000000,
       'gasPrice': 234567897654321,
       'nonce': 0,
       'chainId': 1
}
key = '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318'
signed = w3.eth.account.signTransaction(transaction, key)
signed.rawTransaction
HexBytes('0xf86a8086d55698372431831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca008025a009ebb6ca057a0535d6186462bc0b465b561c94a295bdb0621fc19208ab149a9ca0440ffd775ce91a833ab410777204d5341a6f9fa91216a6f3ee2c051fea6a0428')
signed.hash
HexBytes('0xd8f64a42b57be0d565f385378db2f6bf324ce14a594afc05de90436e9ce01f60')
signed.r
4487286261793418179817841024889747115779324305375823110249149479905075174044
signed.s
30785525769477805655994251009256770582792548537338581640010273753578382951464
signed.v
37

Pero no entiendo algunos de los valores que deben pasarse al transaction.

nonce¿ Qué es chainId?

Usando tales datos:

transaction = {
    'to': '0xe980e77404ae62ab0f2d6b8510bd951e25185414',
    'value': value,
    'gas': 2000000,
    'gasPrice': 1000000000,,
    'nonce': 0,
    'chainId': 1
}

me sale este error:

TypeError: Transaction had invalid fields: {'to': '0xe980e77404ae62ab0f2d6b8510bd951e25185414'}

Luego, traté de usar ecdsa

Pero no pude encontrar un ejemplo simple. No entiendo nada de criptografía, y necesito la forma más fácil de enviar una transacción a la red sin tener que revisar los detalles.

ACTUALIZAR

Traté de evitar usar w3, ya que solo necesito firmar una transacción.

Pude hacer esto para crear una firma:

importar hashlib importar binascii

importar ecdsa

def sign(privkey, message):
    sk = ecdsa.SigningKey.from_string(
        binascii.unhexlify(privkey),
        curve=ecdsa.SECP256k1,
        hashfunc=hashlib.sha256
    )
    signature = binascii.hexlify(
        sk.sign(
            binascii.unhexlify(message),
            hashfunc=hashlib.sha256
        )
    )
    return signature

Y la función funciona bien, devuelvo una cadena de bytes como esta:

b'4ace74a83082b5fc6d356083932b493e9fafbe0fabca19be3e77ccda188d08c6352f1fccb144e18b95a3f7e18f71fe95d79a9dd8ad9ecaa7490315f06a00177f'

Pero la longitud de esta línea es de 128 caracteres y, en el ejemplo del blockcypher, una cadena de 148 caracteres.

Después de intentar enviar una transacción firmada a la red, recibo esta respuesta:

{'error': 'Could not compute an address from provided signature: invalid transaction v, r, s values.'}

Lo más probable es que esté creando la firma incorrecta, pero ¿cómo hacer la correcta?

Parece que estoy casi allí, pero todavía no está funcionando.

Respuestas (2)

La versión 4.x de web3.py requiere que todas las direcciones usen una suma de verificación EIP 55 adecuada . En mis pruebas, recibo el mismo error que usted hasta que convierto la dirección al formulario de suma de verificación correcto (tenga en cuenta las mayúsculas):

0xe980E77404ae62aB0F2d6b8510BD951e25185414

(Tenga en cuenta que también tiene una coma adicional que hace que su código sea sintácticamente inválido, pero supongo que es solo un error de copiar/pegar).

Es un poco engañoso para la dirección que se muestra en etherscan, ya que la mostraron en minúsculas, lo que no cumple con EIP 55.

Gracias @smarx por la pista.

Sin embargo, me las arreglé para encontrar otra solución:

from bitcoin import ecdsa_raw_sign
from bitcoin import der_encode_sig

def signing(transaction, privkey):
    signature = der_encode_sig(*ecdsa_raw_sign(transaction, privkey))
    return signature