[dApp] dMessenger – Chat / Libro de visitas descentralizado
Hola etherians!
Hoy estuve programando algunos Contratos Inteligentes en Solidity para que la comunidad pudiera adquirir entusiasmo y entender que cosas podemos hacer más o menos con Ethereum y Soldity.
Me dio nostalgia recordar que hace muchos años, cuando recién comenzaba a programar, una de las prácticas más típicas entre los programadores de los foros donde participaba, era desarrollar un libro de visitas o chat donde se almacenaban los mensajes y eran mostrados posteriormente en otra sección del sitio web. Conforme evolucionaban los lenguajes de programación, también lo hacían las formas de realizar este mismo ejercicio añadiendo una nueva tecnología o característica. En esos tiempos se tenía que enviar la información a través de un formulario que recargaba la web por cada vez que enviábamos un mensaje al chat o libro de visita, algo muy rústico para lo que es la programación hoy en día. Luego con el pasar de los meses, aparecían programadores futuristas que comenzaron a mostrar la misma práctica del libro de visitas/chat, pero esta vez los visitantes podían escribir y ver los mensajes sin tener que recargar el sitio web, esto gracias a AJAX, una tecnología muy de moda en ese entonces. Así que pensé…
¿Qué tan complicado podría ser crear un libro de visitas/chat descentralizado?
Me puse manos a la obra (código) ¡ Y mi sorpresa fue estremecedora! Realizar este ejercicio con un Contrato Inteligente programado en Solidity como Base de Datos publicada en la Blockchain Ethereum fue mil veces más sencillo que tener que crear una Base de Datos SQL tradicional y tener que interactuar con ella mediante PHP o cualquier otro lenguaje de programación. Adiós SELECT * FROM mensajes, adiós vulnerabilidades SQL Injection, adiós borrar los mensajes sin querer por no ponerle el WHERE al DELETE FROM!!
Antes de empezar…
Les recomiendo leer la documentación oficial de la librería Web3.js de Javascript, que es la que nos permitirá interactuar con la Blockchain desde el navegador. Es importante que estén un poco interiorizados con esta librería para tener un buen desempeño desarrollando los Contratos Inteligentes.
Manos al código
Esta vez haremos la siguiente práctica: Primero pondré el código y posteriormente explicaré que hace cada línea del contrato.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
pragma solidity ^0.4.18; contract dMessenger { address public creador; uint public contadorMensajes = 0; struct Mensaje { address emisor; string mensaje; uint fechaPublicacion; } mapping(uint => Mensaje) public mensajes; function dMessenger () public { creador = msg.sender; } function escribirMensaje(string mensaje) public { mensajes[contadorMensajes].emisor = msg.sender; mensajes[contadorMensajes].mensaje = mensaje; mensajes[contadorMensajes].fechaPublicacion = block.timestamp; contadorMensajes++; } } |
Explicación línea por línea del Contrato Inteligente
Con pragma solidity ^0.4.18;Definimos la versión del compilador de Solidity que utilizamos. En este caso la versión es la 0.4.18
Definimos el nombre del contrato con contract dMessenger { Todo lo que esté dentro de las llaves será parte del código del contrato y la información contenida será pública o privada según como se declaren las variables.
Con address public creador; Creamos una variable pública tipo address para almacenar la dirección Ethereum del creador del contrato. Esto se suele utilizar para poder verificar en un futuro la autoría de un Contrato Inteligente o para permisionar el mismo, de modo que solo el creador (administrador) del contrato sea quién puede realizar ciertas acciones, como transferir ETHER del contrato a una dirección Ethereum (transfer())
uint public contadorMensajes = 0; Nos ayudará a llevar la cuenta de cuántos mensajes han sido publicados, además nos permitirá asociar un ID a cada mensaje, lo que nos facilitará la vida al momento de querer ver un mensaje específico sin tener que cargarlos todos.1 2 3 4 5 6 |
struct Mensaje { address emisor; string mensaje; uint fechaPublicacion; } mapping(uint => Mensaje) public mensajes; |
En Solidity las estructuras se comportan como lo haría un diccionario en Python o un objeto en Javascript, pero que en el caso de este lenguaje se pre-define la estructura que tendrá el diccionario y no se puede alterar luego de creada. Para hacer uso de esta estructura tenemos que usarla como si se tratara de un tipo de dato Mensaje instancia; dónde instancia tendría todos los atributos de Mensaje:
1 2 3 |
instancia.emisor = address; instancia.mensaje = string; instancia.fechaPublicacion = uint; |
Con mapping(uint => Mensaje) public mensajes; lo que estamos haciendo es decirle a Solidity que mensajes será un diccionario donde sus claves serán única y exclusivamente números enteros sin signo o unsigned integer (uint) y que cada clave apuntará a una estructura Mensajes, por lo que cada mensajes[uint] tiene los mismos atributos:
1 2 3 |
mensajes[uint].emisor = address; mensajes[uint].mensaje = string; mensajes[uint].fechaPublicacion = uint; |
En Javascript y Python podemos añadir o eliminar atributos a los objetos, pero en Solidity eso no es posible, ya que la estructura que contiene cada clave en el diccionario, es inmutable. No podemos hacer mensajes[contadorMensajes].nuevoAtributo = nuevoValor;, ya que nuestra estructura Mensaje al no tener el atributo nuevoAtributo arrojaría error y no compilaría el Contrato Inteligente.
1 2 3 |
function dMessenger () public { creador = msg.sender; } |
1 2 3 4 5 6 |
function escribirMensaje(string mensaje) public { mensajes[contadorMensajes].emisor = msg.sender; mensajes[contadorMensajes].mensaje = mensaje; mensajes[contadorMensajes].fechaPublicacion = block.timestamp; contadorMensajes++; } |
1 |
mensajes[contadorMensajes].emisor = msg.sender; |
Bien, vamos paso por paso. Una persona envía un mensaje al contrato llamando escribirMensaje(); lo que pasa es lo siguiente:
El contrato tiene una variable contadorMensajes que es un entero sin signo/unsigned integer (uint) y su valor inicial es 0, por lo que el contrato haría:
1 2 3 4 |
mensajes[contadorMensajes].emisor = msg.sender; mensajes[contadorMensajes].mensaje = mensaje; mensajes[contadorMensajes].fechaPublicacion = block.timestamp; contadorMensajes++; |
Eso en la primera ejecución sería equivalente a:
1 2 3 4 |
mensajes[0].emisor = msg.sender; mensajes[0].mensaje = mensaje; mensajes[0].fechaPublicacion = block.timestamp; contadorMensajes++; |
Dónde
mensajes[contadorMensajes].emisor = msg.sender; escribe en la clave contadorMensajes (0) del diccionario mensajes que el emisor es igual a quién envía la transacción.
Luego con
mensajes[contadorMensajes].mensaje = mensaje; le asignamos un valor al atributo mensaje de la estructura que está alojada en la clave contadorMensajes (0) del diccionario mensajes.
1 |
mensajes[contadorMensajes].fechaPublicacion = now; |
Cuando envíen otro mensaje habrá otra ejecución de la función escribirMensaje(); y contadorMensajes ahora tiene un valor distinto al inicial, ya que luego de añadir la clave contadorMensajes (0) al diccionario mensajes con sus respectivos atributos (emisor, mensaje, fechaPublicación), hicimos un contadorMensajes++;, así que ahora contadorMensajes vale 1 y el siguiente mensaje tendrá ese valor como clave en el diccionario.
Ahora el contrato tiene una variable contadorMensajes que es un entero sin signo/unsigned integer (uint) y su valor pasó a ser 1, por lo que el contrato haría:
1 2 3 4 |
mensajes[contadorMensajes].emisor = msg.sender; mensajes[contadorMensajes].mensaje = mensaje; mensajes[contadorMensajes].fechaPublicacion = block.timestamp; contadorMensajes++; |
Qué es equivalente a:
1 2 3 4 |
mensajes[1].emisor = msg.sender; mensajes[1].mensaje = mensaje; mensajes[1].fechaPublicacion = block.timestamp; contadorMensajes++; |
Dónde
mensajes[contadorMensajes].emisor = msg.sender; escribe en la clave contadorMensajes (1) del diccionario mensajes que el emisor es igual a quién envía la transacción.
Luego con
mensajes[contadorMensajes].mensaje = mensaje; le asignamos un valor al atributo mensaje de la estructura que está alojada en la clave contadorMensajes (1) del diccionario mensajes.
1 |
mensajes[contadorMensajes].fechaPublicacion = now; |
Así sería sucesivamente con todos los mensajes…
Compilar y publicar el Contrato Inteligente
Para compilar y publicar nuestro Contrato Inteligente haremos uso de una aplicación web que personalmente me encanta, Remix – Solidity IDE. No requiere descargar nada más que Metamask para su correcto funcionamiento. Nos permitirá publicar contratos e interactuar con ellos muy fácilmente.
Publicar contratos en la red Ethereum conlleva un gasto en ETHER, por lo que antes de publicar algo, tenemos que fijarnos que todo esté muy bien programado, porque volver a subir un contrato nos podría salir caro… Quizás algunos no lo saben, pero existen redes de prueba dentro de la red Ethereum que nos permitirán ejecutar contratos inteligentes sin gastar ETHER real. La que utilizo siempre es Rinkeby y pueden obtener muchos ETHER gratis dando clic aquí. En la parte de la dirección deben poner la cuenta Ethereum que aparece en su plugin Metamask.
Abrimos Metamask y veremos que estamos en la Main Net, que es la red real de Ethereum, donde publicar contratos inteligentes tiene un costo real de ETHER
Damos clic en la esquina superior izquierda y seleccionamos Rinkeby
Bien, una vez seleccionada la red Rinkeby en Metamask, vamos al compilador online Remix – Solidity IDE donde inicialmente veremos un contrato inteligente que viene pre-cargado en el IDE:
En la esquina superior izquierda se ve un » + » que al darle clic nos permitirá editar más archivos. Le damos clic y creamos «dMessenger.sol»
Damos clic en OK y ya podemos comenzar a editar nuestro código Solidity. Pegamos el código del dMessenger y debería lucir exactamente así:
El compilador online por defecto va compilando sin preguntar, por lo que no es necesario estar dando clic a un botón de compilar para ver si arroja errores, ya que los va mostrando en tiempo real, algo que acelera bastante el proceso. Vamos a la pestaña Run que es desde dónde podremos llevar el contrato a la red de pruebas. Nos aparecerán distintos entornos (Environments) donde ejecutar nuestro Contrato Inteligente:
El que nos interesa es Injected Web3, ya que Metamask funciona inyectando Web3.js en el navegador.
Nota importante: Si Metamask está bloqueado con contraseña no podremos utilizarlo y nos aparecerá así:
Asumiendo que tenemos el Metamask desbloqueado, que estamos con Metamask en la red Rinkeby y que en la misma tenemos los ETHER obtenidos en Rinkeby.io, vamos a comenzar con la publicación del contrato inteligente. Los campos Gas Limit, Gas Price y Value los dejamos tal como están.
Nota:Para que un contrato sea publicado es necesario que quién lo publica lo haga a través de una transacción.
Damos clic en Create, lo que nos abrirá el Metamask para que firmemos la transacción y el contrato sea publicado. En la misma ventana se indican los costos de publicar el contrato en esa red, pero no nos interesa porque nuestros ETHER son gratuitos en Rinkeby 😀
Aprobamos la transacción dando clic en el botón verde que dice Submit y el contrato se publicará en la red Rinkeby de Ethereum. Esperamos unos segundos y nos aparecerá la interfaz para interactuar con las variables y funciones públicas del contrato.
Si bien es posible añadir y consultar mensajes desde esta interfaz, no es lo más óptimo, así que crearemos nuestra propia interfaz web (rústica) para este cometido.
Para poder invocar nuestro contrato inteligente desde un sitio web requerimos 2 cosas:
- Address: La dirección del contrato inteligente.
- ABI: Application Binary Interface
Para obtener la dirección del contrato damos clic en el botón de Copiar y lo apuntamos en un lugar donde no lo perdamos.
Para encontrar el ABI de nuestro contrato debemos ir a la pestaña Compile, luego dar clic en Details y buscar donde dice INTERFACE – ABI
Copiamos dando clic en el botón de Copiar y también lo apuntamos en un lugar donde podamos obtenerlo posteriormente, porque es vital para el siguiente paso, la creación de la interfaz web.
¿Cómo interactuar con el contrato inteligente desde un sitio web?
Para interactuar con el Contrato Inteligente (cargar los mensajes y poder escribir en él) requerimos 3 cosas:
- HTML para la interfaz gráfica: Un INPUT donde escribir el mensaje, un botón para enviarlo y un div donde cargar los mensajes enviados.
- Metamask: Es un plugin que nos integra un nodo Ethereum en el navegador. Nos permitirá ejecutar e interactuar con la Blockchain de Ethereum con Javascript.
- Javascript: Con este lenguaje de programación invocaremos Contratos Inteligentes (y todas sus funciones públicas) que vivan en la red Ethereum.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!DOCTYPE HTML> <html> <head> <title>[dApp] dMessenger - Ethereum Chile</title> <meta charset="utf8" /> </head> <body> <div id="interfaz"> <h1>[dApp] dMessenger - Libro de visitas descentralizado</h1> Tu mensaje: <input type="text" name="mensaje" placeholder="Mensaje"> <input type="submit" onclick="escribir()" value="Escribir mensaje"> </div> <hr /> <div id="contenido"> </div> <script src="https://code.jquery.com/jquery-latest.js"></script> <script src="main.js"></script> </body> </html> |
¿Cómo invocar un contrato inteligente?
Como Metamask inyecta web3.js en el navegador, nosotros solo debemos utilizarlo:
1 2 3 4 |
var abi = []; // acá el ABI de tu contrato. var addressContrato = "0x"; // acá tu la dirección (address) del contrato. var contrato = web3.eth.contract(abi); var funciones = contrato.at(addressContrato); |
En mi caso quedaría
1 2 3 4 |
var abi = [{"constant":true,"inputs":[],"name":"contadorMensajes","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"mensajes","outputs":[{"name":"emisor","type":"address"},{"name":"mensaje","type":"string"},{"name":"fechaPublicacion","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"creador","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"mensaje","type":"string"}],"name":"escribirMensaje","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; var addressContrato = "0x5ae44cc11fcb91b209a2848b0f3bdcf1a870d2d9"; var contrato = web3.eth.contract(abi); var funciones = contrato.at(addressContrato); |
Con este sencillo código lo que estamos haciendo es cargar todas las variables y funciones públicas del contrato (que viven en la dirección addressContrato) y cargándolas en la variable funciones.
¿Cuáles son estas variables y funciones públicas?
En el caso de nuestro contrato contamos con las variables públicas:
- creador (address)
- contadorMensajes (uint)
- mensajes (string)
Las funciones públicas:
- dMessenger
- escribirMensaje(string mensaje)
Por lo que desde la variable funciones tendremos acceso tanto como a las funciones como a los valores de las variables, siempre y cuando sean públicas.
Nota: Quisiera destacar que para leer la información de cualquier contrato inteligente en Ethereum no tiene costo alguno, ya que no hay cambios de estados en las variables y no conlleva un gasto en GAS. Lo único que tiene costo es modificar o publicar información en los contratos y nunca se incurrirá en un gasto si tu no firmas la transacción.
Leer contrato = gratis.
Modificar contrato = cobro en ETHER.
Bien, ahora la variable funciones tiene acceso a las variables y funciones mencionadas anteriormente, pero cómo leo sus valores? Tanto para llamar funciones como para leer valores de variables requerimos de callbacks. Por ejemplo, para obtener el número de mensajes publicados:
1 2 3 4 5 6 7 8 9 10 |
var callback = function(error, respuesta){ if(error) throw error; console.log(respuesta.c[0]) } funciones.contadorMensajes(callback); // O pasando el callback directo funciones.contadorMensajes(function(error, respuesta){ if(error) throw error; console.log(respuesta.c[0]) }); |
Siempre que vayamos a consultar una variable pública tipo uint, debemos añadir .c[0] a la respuesta, porque nos devuelve un objeto cuya clave c contiene un array en el cuál el índice 0 es el que contiene la información que necesitamos.
Si quisieramos escribir un mensaje tendríamos que llamar a la función escribirMensaje(); del contrato inteligente. Eso lo hacemos de la siguiente forma:
1 2 3 4 5 6 7 8 9 10 |
var callback = function(error, respuesta){ if(error) throw error; console.log(respuesta); } funciones.escribirMensaje("Tu mensaje", callback); // Aunque escribirMensaje requiera un parámetro, también le podemos pasar un callback directo... funciones.escribirMensaje("Tu mensaje", function(error, respuesta){ if(error) throw error; console.log(respuesta); }); |
Como escribirMensaje(); añade mensajes al contrato, estamos modificando el estado, por ende añadir mensajes nos conlleva un costo en ETHER, así que siempre que vayamos a publicar un mensaje, Metamask nos pedirá que firmemos la transacción. La respuesta de las funciones que modifican los estados del contrato siempre dan como respuesta el hash de la transacción donde fue minado.
Archivo main.js terminado y comentado para quienes tengan dudas…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
var abi = [{"constant":true,"inputs":[],"name":"contadorMensajes","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"mensajes","outputs":[{"name":"emisor","type":"address"},{"name":"mensaje","type":"string"},{"name":"fechaPublicacion","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"creador","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"mensaje","type":"string"}],"name":"escribirMensaje","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]; var addressContrato = "0x5ae44cc11fcb91b209a2848b0f3bdcf1a870d2d9"; var contrato = web3.eth.contract(abi); var funciones = contrato.at(addressContrato); var contadorMensajes = 0; function escribirMensaje(){ var mensaje = $("input[name=mensaje").val(); // obtengo el valor del input if(mensaje.length > 0){ funciones.escribirMensaje(mensaje, function(error, respuesta){ // intento escribir el mensaje. Se o Metamask pidiendo firmar la transacción... if(error) throw error; // error, no se firmó la transacción alert("Mensaje enviado!!"); //... }) } } function leerMensajes(){ // función para leer los mensajes del contrato jQuery("div#contenido").html(''); // limpiamos el div#contenido con los mensajes antes de volver a cargarlos funciones.contadorMensajes(function(error, respuesta){ // llamo la variable pública contadorMensajes del contrato... if(error) throw error; contadorMensajes = respuesta.c[0]; // guardo el número de mensajes publicados }) for(var i = 0; i < contadorMensajes; i++){ // recorro de 0 hasta contadorMensajes funciones.mensajes(i, function(error, respuesta){ // cargo los mensajes desde 0 a contadorMensajes if(error) throw error; /* respuesta es un array con la estructura Mensaje de Solidity: índice 0: emisor; índice 1: mensaje; índice 2: fechaPublicacion; */ var emisor = respuesta[0]; var mensaje = respuesta[1].replace("<", ""); // evitamos XSS mensaje = mensaje.replace(">", ""); // evitamos XSS var fechaPublicacion = respuesta[2]; jQuery("<b>Emisor</b>: "+emisor+"<br /><b>Mensaje</b>: "+mensaje+"<br /><b>Fecha</b>: "+ts2date(fechaPublicacion)+"<hr/>").appendTo("#contenido") // cargo la estructura del mensaje en el div #contenido... }) } } function ts2date(ts){ // función que convierte timestamp a fecha. var a = new Date(ts * 1000); var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; var year = a.getFullYear(); var month = months[a.getMonth()]; var date = a.getDate(); var hour = a.getHours(); var min = a.getMinutes(); var sec = a.getSeconds(); var time = date + ' ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec ; return time; } window.setInterval("leerMensajes()", 2000); // busca nuevos mensajes cada 2 segundos... |
Si quisieramos cargar un mensaje en específico, como el mensaje n°50, solo tendríamos que hacer
1 2 3 4 5 6 7 8 9 |
funciones.mensajes(50, function(error, respuesta){ if(error) throw error; /* respuesta es un array con la estructura Mensaje de Solidity: índice 0: emisor; índice 1: mensaje; índice 2: fechaPublicacion; */ console.log(respuesta); }); |
Recordemos que en nuestro código Solidity mensajes era un diccionario/mapping(); público que nos decía que las claves de éste, serían números enteros sin signo/unsigned integer (uint) y que cada clave apuntaría a una estructura Mensaje…
Si quieren ver este ejemplo funcionando en la red Rinkeby, pueden dejarnos su mensaje (es gratis!!) dando clic aquí.
Espero haya sido de su agrado y si algo no se entendió por favor dejar un comentario con la pregunta 😀
Saludos!!
Hola, gracias por el tutorial está genial!
Cuando escribo el mensaje me aparece mensaje en el REMIX browser, a que se debe..??
transact to browser/dMessenger.sol:dMessenger.escribirMensaje errored: Error encoding arguments: SyntaxError: Unexpected token H in JSON at position 1
Hola John!
Cuando hay funciones que reciben un parámetro que no es uint, debes ponerlo entre comillas. Recuerda también que si la función requiere más de 1 parámetro, deberás separar por una coma cada uno de ellos y poner comillas a los que lo requieran.
Saludos 😀 .
Muy bien me parece muy didactico
¡ Muchas gracias 😀 !
Tremendo el tutorial, aun no lo veo completo, pero está muy bien explicado, se parece mucho a la lógica de Java para programar
Hola Francisco, muchas gracias por tus palabras.
La lógica en Solidity es idéntica a la lógica en casi todos los lenguajes de programación, solo hay que aprender a llevar bien las funciones internas del lenguaje y sus buenas prácticas. Atento que se viene nuevo tutorial sobre aplicaciones descentralizadas!
Saludos 😀
Hola, tremendo aporte! muchas gracias. Pude completar todo el tutorial pero no logro invocar el contrato desde el .html, coloque el numero de cuenta que me da el metamask en el .js y el ABI pero al ejecutar el .html abre la pagina, coloco un texto en comillas pero no tienen ningún efecto. Muchas gracias.
Hola Enrique!
Tuve el mismo problema alguna vez y lo solucioné montando el .html de la interfaz en un servidor local tipo Wamp, ya que Metamask me tiraba error al intentar inyectar en file://
Saludos 😀
hola ethe hize todo al pie de la letra y no me sale nada esta es la web http://buyiota.webcindario.com/contrato.html alguna ayuda ? que paso el contrato ya se publico
https://rinkeby.etherscan.io/tx/0x6ff135fc75cd5b3f46859028dc4820166784c84f417a9949f1b9f1fea3fa6eb7
que hize mal ? como hago para enviar mensaje
Hola Carlos!
La dirección de tu contrato inteligente es 0x4cf32ff985cb64309fad6610fcba058641db18c9 y en tu archivo main.js indicaste otra (0xdaee045b86d033dae69cfd42cb2f4d26a7333532).
Saludos 😀
Hola muy buenas, cuando pongo la dirección de mi wallet en Rinkeby me pone que el url no es válido, después de haber hecho lo de cambiarlo a Rinkeby.
Muchas gracias de antemano.
un buen ejemplo de messenger descentralido. Pero creo que mirando las transacciones de este contrato podemo leer toda la conversación. Estoy en lo correcto?
He visto algun ejemplo que se encripta el mensaje con la clave publica del destinatario, y el destinatario puede desencriptar con su llave privada. Me pregunto también, si mi razonamiento es bueno?
Hola Josep!
Efectivamente, lo que dices es correcto. Es más, aunque tu declares las variables como privadas, igual un minero que haya minado la transacción de tu contrato podría mirar dentro del contrato y ver sus variables y valores. Respecto a lo del cifrado, estás en lo correcto 😀 .
Muchas gracias por tu tiempo e interés!
Saludos, Javier Guajardo 🙂
como se podria obtener el thash cada vez que se envia un mensaje?
Hola Javier !
Excelente aporte, acabo de leer todo el tutorial. Ahora voy a poner en práctica el ejemplo.
Te escribo cualquier duda.
Saludos desde Mendoza, Argentina.
Hola Javier !
Acabo de realizar el tutoria. Lo puede hacer sin problemas en entorno Linux, si alguno tiene dudas o dificultades no dude en escribir
Gracias por el aporte.
Hola como estas?, no logro hacerlo funcionar, veo que arroja un error. Verifique tu aplicación y veo que arroja el mismo error. Espero que me puedas ayudar
inpage.js:1 Uncaught Error: invalid address
at c (inpage.js:1)
at inputTransactionFormatter (inpage.js:1)
at inpage.js:1
at Array.map ()
at i.formatInput (inpage.js:1)
at i.toPayload (inpage.js:1)
at n.e [as sendTransaction] (inpage.js:1)
at u.sendTransaction (inpage.js:1)
at u.execute (inpage.js:1)
at escribirMensaje (main.js:171)
Hola Javier: Muy bueno el tutorial Lo hice paso a paso y funciono! el único problema que tuve cuando quise crear un nuevo mensaje me da un error en main.js:
inpage.js:1 Uncaught Error: invalid address
at c (inpage.js:1)
at inputTransactionFormatter (inpage.js:1)
at inpage.js:1
at Array.map ()
at i.formatInput (inpage.js:1)
at i.toPayload (inpage.js:1)
at _.e [as sendTransaction] (inpage.js:1)
at u.sendTransaction (inpage.js:1)
at u.execute (inpage.js:1)
at escribirMensaje (main.js:101)
El mismo error da en tu ejemplo online:
Puede ser que haya cambiado algo en la apiJS?
Gracias y felicitaciones!