Cómo salvar a sus usuarios de Ethereum Dapp de pagar gasolina por transacciones

Y en cambio, usted como propietario de un dapp lo paga

Uno de los puntos de fricción para la adopción de Ethereum dapp es que los usuarios tienen que pagar el gas (tarifa txn) para registrar sus transacciones en la cadena de bloques. Por ejemplo, tengo un dapp de votación simple que permite que cualquiera vote por los candidatos y los votos se almacenan en la cadena de bloques. Un usuario que quiera registrar su voto en la cadena de bloques debe pagar una tarifa de transacción / gas. Esto no es ideal porque, como propietario de un dapp, espera que los usuarios de su aplicación tengan Ether para pagar el combustible cuando lo único que quieren hacer es realizar una acción simple que no tiene nada que ver con la transferencia de dinero. Pero si la transacción necesita ejecutarse en la cadena de bloques, no hay otra opción que pagar la tarifa. ¿Qué pasaría si hubiera una forma para que los usuarios ejecuten transacciones de forma segura (vote por un candidato en nuestro ejemplo) y deje que otra persona (potencialmente propietario del contrato) registre la transacción en la cadena de bloques y pague por ella?

Gracias a este tweet de John Backus que tenía la información suficiente para ayudarme a implementar una solución para mi dapp de votación.

El código de trabajo completo está aquí: https://github.com/maheshmurthy/ethereum_voting_dapp/tree/master/chapter4

La aplicación de demostración está aquí: https://www.zastrin.com/voting-dapp-without-paying-gas.html

Quería compartir detalles sobre cómo implementé esta solución para mi dapp simple para que más personas puedan adoptar esta técnica en sus propios dapps y espero poder mejorarla. Esta publicación cubre lo siguiente:

  1. Una descripción general de muy alto nivel de la criptografía de clave pública y las firmas digitales que son clave para comprender esta solución
  2. Los detalles de la solución y el nuevo flujo de aplicaciones.
  3. Detalles de implementación (frontend js y código de contrato de Solidity)
  4. Discutir posibles problemas y mejoras

Firmas digitales

Para que esta solución tenga sentido, necesitará una comprensión básica de cómo funcionan las firmas digitales en criptografía. Siéntase libre de omitir esta sección si conoce la criptografía de clave pública. Trataré de explicar el concepto de claves públicas / privadas y firmas digitales a un nivel muy alto, pero recomiendo aprender más en detalle: wikipedia es un buen lugar para comenzar.

La criptografía de clave pública es un sistema criptográfico en el que tiene 2 claves: clave pública (Pu) y una clave privada (Pr). Usted entrega su clave pública al mundo entero y guarda la clave privada para usted. Por ejemplo: su dirección Ethereum es una clave pública (en realidad se deriva de la clave pública, pero para este ejercicio, pensemos en ella como clave pública) y su clave privada se almacena en su navegador o en su teléfono / computadora. Como sabes, para que alguien te envíe Ether, solo necesitan saber tu dirección pública (cuenta). Sin embargo, solo usted puede acceder a los fondos que posee porque usted es el único que conoce su clave privada.

La criptografía de clave pública tiene algoritmos que le permiten cifrar, descifrar, firmar y verificar mensajes utilizando su par de claves.

Veamos qué significa firmar y verificar un mensaje a través de un ejemplo. Digamos que el usuario Kim tiene un par de claves públicas / privadas

Pu = "0x44ac12c1e3dfd8edaf83b6f65918229d5279a6f5"

Pr = "dbc226043e390cf39280e5edfd418d7ad61931c76509270867d300f110c46506"

Para firmar un mensaje, Kim ejecuta un signo de función ("Vote por Alice", Pr) que genera una cadena alfanumérica

firma = 0x9127112de0033555c7f6508d963d484965a953844dfcff092712102c236467a25af57edc53b63880ea39af8ce7334f6d77a8206e805305e7c6ad919d12bfae5c1b

Esta es la firma digital del mensaje "Vote por Alice" firmado por Kim usando su clave privada Pr.

Ahora cualquiera puede verificar que el mensaje "Vote por Alice" fue firmado por Kim ejecutando la función de verificación, verificar ("Vote por Alice", firma) que genera "0x44ac12c1e3dfd8edaf83b6f65918229d5279a6f5". Si se da cuenta, esa salida es la clave pública de Kim Pu (recuerde, todos saben que es la clave pública de Kim), lo que significa que el mensaje fue firmado definitivamente por Kim. Si manipula la firma o el mensaje (al cambiar incluso un carácter), el algoritmo de verificación genera una clave pública completamente diferente y sabrá que el mensaje fue manipulado porque la clave pública será diferente de Pu.

Detalles de la solución

Si comprende las firmas digitales, la solución es extremadamente trivial. Veamos cómo se puede usar en nuestra aplicación de votación para evitar que los usuarios paguen la tarifa del gas sin comprometer su voto. Puede ver a continuación todos los usuarios de dapp y las acciones que realizan.

  1. Un votante indica su intención de votar por un candidato firmando un mensaje usando su clave privada. No enviarán su transacción a blockchain, por lo que no se paga ninguna tarifa de txn. La cola de mensajes en el diagrama anterior es solo una ubicación fuera de la cadena donde se almacenan todos los detalles de la votación.
  2. Cualquiera que esté dispuesto a pagar una tarifa de txn (generalmente el propietario del contrato) toma la firma, el nombre del candidato y la dirección de la cuenta del votante y los envía a la cadena de bloques.
  3. El contrato inteligente utiliza la función de verificación para derivar la clave pública (dirección de la cuenta de Ethereum) en función del nombre y la firma del candidato. Si la clave pública derivada coincide con la dirección del usuario que firmó el mensaje, registra el voto o de lo contrario falla la transacción.

Detalles de implementacion

Veamos ahora la implementación real y cómo encajan todas las piezas.

Paso 1: firma el mensaje

El primer paso es firmar el mensaje como votante. Utilizaremos la función eth_signTypedData para firmar nuestro mensaje. Esta función se ha implementado en Metamask, lo que hace que sea muy fácil firmar mensajes. Puede encontrar más detalles y discusiones sobre esta propuesta aquí: https://github.com/ethereum/EIPs/pull/712. Puede encontrar el código para firmar el mensaje a continuación.

Una cosa realmente importante a tener en cuenta es que internamente eth_signTypedData codifica el mensaje y el mensaje codificado es lo que se firma. Puede consultar la función typedSignatureHash aquí para obtener más detalles sobre el hash.

Paso 2: envíe el voto firmado a blockchain

Como se trata solo de una aplicación de demostración, no almacenamos la firma ni otros detalles en ningún lado. Se muestra directamente en la página después de firmar el mensaje. Cualquiera puede tomar estos detalles y enviarlos a blockchain. Aquí está el código que envía el voto a la cadena de bloques:

Paso 3: Verifique los detalles del voto en el contrato inteligente

Ahora verificamos en el contrato inteligente si la información de voto enviada es válida y registramos el voto.

Zeppelin tiene una práctica biblioteca llamada ECRecovery que usamos para verificar el mensaje firmado. La función voteForCandidate verifica el mensaje firmado (función de recuperación) y actualiza el recuento de votos si la verificación se realiza correctamente.

Si recuerdas, mencioné anteriormente que eth_signTypedData agrega el mensaje ("Vote por Alice") antes de firmarlo. La función de recuperación de solidez no tiene ningún conocimiento de la función de hash utilizada en eth_signTypedData y, por lo tanto, no puede verificar el mensaje "Vote por Alice". Tiene que generar el hash del mensaje "Vote por Alice" y luego verificarlo. En lugar de generar el hash dentro del contrato, hacemos un hash previo de todos los mensajes de antemano y lo pasamos al constructor para que sea fácil de buscar al verificar. El código para generar el hash está en el archivo de migración a continuación

¡Ese es todo el código que necesitas para que la nueva aplicación funcione!

Creé una demostración rápida para mostrar cómo funciona esta aplicación

El código de trabajo completo está aquí: https://github.com/maheshmurthy/ethereum_voting_dapp/tree/master/chapter4

La aplicación de demostración está aquí: https://www.zastrin.com/voting-dapp-without-paying-gas.html

Posibles problemas para abordar

Hay algunos problemas a tener en cuenta al construir un dapp real utilizando esta técnica. Algunos de ellos se enumeran a continuación:

  1. ¿Dónde se almacenan los mensajes firmados? Puede usar algún tipo de sistema de colas para almacenar estos mensajes.
  2. ¿Cuál es la garantía de que el mensaje firmado finalmente se envíe a la cadena de bloques?
  3. Almacenar el hash de todos los mensajes en la cadena de bloques no es ideal, entonces, ¿cuál es la mejor solución para ello?

Si tiene ideas sobre cómo abordar estos problemas o si ve alguna falla en esta solución, ¡deje un comentario!

Nota: Aparentemente, la API eth_signTypedData todavía no es estable y solo ha sido implementada por metamask. Tenga cuidado con esto si planea usar esta técnica en mainnet / producción.

Otras lecturas

https://en.wikipedia.org/wiki/Public-key_cryptography

https://en.wikipedia.org/wiki/Digital_signature

https://github.com/danfinlay/js-eth-personal-sign-examples/

https://danfinlay.github.io/js-eth-personal-sign-examples/

https://github.com/ethereum/EIPs/pull/712

Gracias Chris Whinfrey y Febin John James por revisar los borradores de este artículo.

Aprende más

Si está interesado en aprender a construir aplicaciones descentralizadas de Ethereum, tengo algunos cursos interesantes en www.zastrin.com

Si desea recibir una notificación cuando escriba más tutoriales, puede suscribirse aquí.