Inmersión en desarrollo en Ethereum. Parte 5: Oraclize

El acceso a archivos grandes y diversos datos dinámicos externos son a menudo una parte muy importante de una aplicación descentralizada. Al mismo tiempo, Ethereum en sí no proporciona un mecanismo para dar la vuelta: los contratos inteligentes solo se pueden leer y escribir dentro de la propia cadena de bloques. En este artículo, consideraremos Oraclize, que solo permite interactuar con el mundo exterior al consultar casi cualquier recurso de Internet. Un tema relacionado es IPFS, y lo mencionamos brevemente.



IPFS


IPFS es un sistema de archivos distribuido con direccionamiento de contenido. Esto significa que para el contenido de cualquier archivo agregado allí, se considera un hash único. Luego se usa el mismo hash para buscar y recuperar este contenido de la red.
La información básica ya se ha descrito en este artículo y en varios otros, por lo que no vemos motivos para repetir.

¿Por qué usar IPFS junto con Ethereum?


Guardar cualquier volumen de contenido en la cadena de bloques es demasiado costoso y perjudicial para la red. Por lo tanto, la mejor opción es guardar algún tipo de enlace al archivo que se encuentra en el almacenamiento fuera de la cadena, no necesariamente IPFS. Pero IPFS tiene una serie de ventajas:

  • Un enlace de archivo es un hash que es exclusivo de los contenidos específicos del archivo, por lo que si colocamos este hash en la cadena de bloques, podemos estar seguros de que el archivo recibido de él es el que se agregó originalmente, el archivo no se puede reemplazar
  • El sistema distribuido asegura contra la falta de disponibilidad de un servidor específico (debido al bloqueo u otras razones)
  • El enlace al archivo y la confirmación de hash se combinan en una línea, lo que significa que puede escribir menos en la cadena de bloques y ahorrar gas

Entre las deficiencias, se puede mencionar que, dado que no hay un servidor central, para la accesibilidad de los archivos es necesario que al menos un archivo esté "distribuido". Pero si tiene un archivo específico, conectarse a los distribuidores es fácil: inicie su demonio ipfs y agregue el archivo a través de ipfs add .

La tecnología es muy adecuada para la ideología de la descentralización, por lo tanto, considerando Oraclize ahora, a menudo encontraremos el uso de IPFS en diferentes mecanismos de oráculo.

Oraclize


Para realizar casi cualquier trabajo útil, un contrato inteligente necesita recibir nuevos datos. Sin embargo, no hay una capacidad incorporada para satisfacer una solicitud de blockchain al mundo exterior. Por supuesto, puede agregar todo lo que requieren las transacciones manualmente, pero es imposible verificar de dónde provienen estos datos y su confiabilidad. Además, es posible que deba organizar una infraestructura adicional para actualizar rápidamente los datos dinámicos, como los tipos de cambio. Y las actualizaciones con un intervalo fijo provocarán desbordamientos de gas.

Por lo tanto, el servicio provisto por Oraclize es útil: en un contrato inteligente, puede enviar una solicitud a casi cualquier API o recurso en Internet, asegúrese de que los datos recibidos del recurso especificado no cambien y use el resultado en el mismo contrato inteligente.

Oraclize no es solo un servicio de Ethereum, se proporciona una funcionalidad similar a otras cadenas de bloques, sino que solo describiremos el paquete con Ethereum.

Empezando


Todo lo que se necesita para comenzar es agregar uno de los archivos oraclizeAPI del repositorio al proyecto. Solo necesita elegir el adecuado para su versión del compilador (solc): oraclizeAPI_0.5.sol para versiones que comienzan en 0.4.18, oraclizeAPI_0.4.sol para versiones de 0.4.1, oraclizeAPI_pre0.4.sol para todo lo anterior, soporte Esta versión ya ha sido descontinuada. Si usa trufa, no olvide cambiar el nombre del archivo a usando Oracle: requiere que el nombre del archivo y el contrato coincidan.

Al incluir el archivo apropiado en su proyecto, hereda el contrato de usingOraclize . Y puede comenzar a usar Oracle, que se reduce a dos cosas principales: enviar una solicitud utilizando el asistente oraclize_query , y luego procesar el resultado en la función __callback . El contrato inteligente más simple (para obtener el precio actual del tiempo aire en dólares) podría verse así:

 pragma solidity 0.4.23; import "./usingOraclize.sol"; contract ExampleContract is usingOraclize { string public ETHUSD; event updatedPrice(string price); event newOraclizeQuery(string description); function ExampleContract() payable { updatePrice(); } function __callback(bytes32 myid, string result) { require (msg.sender == oraclize_cbAddress()); ETHUSD = result; updatedPrice(result); } function updatePrice() payable { if (oraclize_getPrice("URL") > this.balance) { newOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee"); } else { newOraclizeQuery("Oraclize query was sent, standing by for the answer.."); oraclize_query("URL", "json(https://api.coinmarketcap.com/v1/ticker/ethereum/?convert=USD).0.price_usd"); } } } 

La función que envía la solicitud es updatePrice . Puede ver que primero hay una comprobación de que oraclize_getPrice(“URL”) mayor que el saldo actual del contrato. Esto se debe a que la llamada oraclize_query debe pagarse, el precio se calcula como la suma de la comisión fija y el pago de gas por llamar a la devolución de llamada. “URL” es una designación de uno de los tipos de fuentes de datos, en este caso es una simple solicitud a través de https, luego consideraremos otras opciones. Las respuestas a pedido se pueden analizar de antemano como json (como en el ejemplo) y de varias otras maneras (lo consideraremos más adelante). Se devuelve una línea de __callback en __callback . Al principio, se verifica que la llamada pasó de la dirección confiable de oraclize

Todas las opciones para usar oraclize se crean de acuerdo con un esquema, solo las fuentes de datos y la capacidad de agregar autenticación a __callback . Por lo tanto, en futuros ejemplos citaremos solo diferencias significativas.

Precio de uso


Como ya se mencionó, el éter adicional se paga por las solicitudes de oraclize, y se elimina del saldo del contrato, y no de la dirección de la llamada. Una excepción es solo la primera solicitud de cada nuevo contrato, se proporciona de forma gratuita. También es interesante que se conserven los mismos mecanismos en las redes de prueba, pero el pago se realiza transmitiendo la red correspondiente, es decir, en testnet las solicitudes son prácticamente gratuitas.

Ya se ha mencionado que el precio de la solicitud consta de dos valores: una comisión fija y el pago de una devolución de llamada de gas. Una comisión fija se define en dólares, y la cantidad de éter se calcula a partir de la tasa actual. La comisión depende de la fuente de los datos y los mecanismos de apoyo adicionales, en los que nos detendremos. La tabla de precios actual se ve así:


Como puede ver, el precio por solicitud de URL es de varios centavos. ¿Es mucho o poco? Para hacer esto, consideremos cuánto cuesta la segunda parte: el cargo de devolución de llamada de gas.
Esto funciona de acuerdo con el siguiente esquema: la cantidad de éter necesaria para pagar una cantidad fija de gas a un precio fijo se transfiere por adelantado con la solicitud del contrato. Esta cantidad debería ser suficiente para hacer una devolución de llamada, y el precio debería ser adecuado para el mercado, de lo contrario la transacción no se realizará o se suspenderá durante mucho tiempo. Al mismo tiempo, está claro que no siempre es posible saber la cantidad de gas por adelantado, por lo tanto, el tablero también debe estar en reserva (la reserva no se devuelve). Los valores predeterminados son un límite de 200 mil de gas a un precio de 20 gwei. Esto es suficiente para una devolución de llamada promedio con varias entradas y algún tipo de lógica. Y el precio de 20 gwei, aunque puede parecer demasiado grande en este momento (al momento de escribir, el promedio es de 4 gwei), pero en el momento de la afluencia de transacciones, el precio de mercado puede saltar repentinamente y ser aún más alto, por lo que, en general, estos valores están cerca de los valores reales utilizados. Entonces, con tales valores y el precio del aire en la región de $ 500, los pagos de gas se acercarán a $ 2, por lo que podemos decir que una comisión fija ocupa una pequeña parte.

Si sabe lo que está haciendo, existe una opción para cambiar el límite y el precio del gas, lo que ahorra significativamente en las solicitudes.

El precio del gas se puede establecer mediante una función separada: oraclize_setCustomGasPrice(< wei>) . Después de la llamada, el precio se guarda y se usa en todas las solicitudes posteriores.
El límite se puede establecer en la consulta oraclize_query , especificándolo con el último argumento, por ejemplo, así:

 oraclize_query("URL", "<>", 50000); 

Si tiene una lógica compleja en __callback y el gas se consume más de 200k, entonces definitivamente deberá establecer un límite que cubra el peor de los casos de consumo de gas. De lo contrario, si se excede el límite, __callback simplemente retrocederá.

Por cierto, recientemente, Oraclize recibió información que puede pagar por solicitudes fuera de blockchain, lo que le permitirá no gastar todo el límite o devolver el saldo (y el pago no es del contrato). No hemos tenido que usar esto todavía, pero oraclize ofrece contactarlos en info@oraclize.it, si esta opción es interesante. Por lo tanto, ten en cuenta.

Como funciona


¿Por qué, habiendo heredado de un contrato inteligente regular, obtenemos una funcionalidad que inicialmente no era compatible con los mecanismos de blockchain? De hecho, el servicio Oracle no solo consiste en contratos con funciones auxiliares. El trabajo principal para obtener datos lo realiza un servicio externo. Los contratos inteligentes forman aplicaciones para acceder a datos externos y ponerlos en la cadena de bloques. Servicio externo: monitorea nuevos bloques de blockchain y, si detecta una aplicación, la ejecuta. Esquemáticamente, esto se puede representar de la siguiente manera:


Fuentes de datos


Además de la URL considerada, oraclize ofrece 4 opciones más (que vio en la sección sobre precios): WolframAlpha , IPFS , random y computation . Consideremos cada uno de ellos.

1. URL


El ejemplo ya discutido utiliza esta fuente de datos. Esta es la fuente de las solicitudes HTTP a varias API. El ejemplo fue el siguiente:

 oraclize_query("URL", "json(https://api.coinmarketcap.com/v1/ticker/ethereum/?convert=USD).0.price_usd"); 

Esto está obteniendo el precio del éter, y dado que api proporciona una cadena json con un conjunto de datos, la solicitud se envuelve en un analizador json y devuelve solo el campo que necesitamos. En este caso, es GET, pero la URL de origen también admite solicitudes POST. El tipo de solicitud se determina automáticamente mediante un argumento adicional. Si hay un json válido como en este ejemplo:

 oraclize_query("URL", "json(https://shapeshift.io/sendamount).success.deposit", '{"pair":"eth_btc","amount":"1","withdrawal":"1AAcCo21EUc1jbocjssSQDzLna9Vem2UN5"}') 

entonces la solicitud se procesa como POST (la API utilizada se describe aquí , si está interesado)

2. WolframAlpha


Esta fuente de datos le permite acceder al servicio WolframAlpha , que puede proporcionar respuestas a varias solicitudes de hechos o cálculos, por ejemplo

 oraclize_query(“WolframAlpha”, “president of Russia”) 

devolverá Vladimir Putin , y solicitará

 oraclize_query(“WolframAlpha”, “solve x^2-4”) 

devolverá x = 2 .
Como puede ver, el resultado fue incompleto porque se perdió el símbolo ±. Por lo tanto, antes de usar esta fuente, debe verificar que el valor de una solicitud específica se pueda usar en un contrato inteligente. Además, la autenticación no es compatible con las respuestas, por lo que oraclize recomienda que esta fuente se use solo para pruebas.

3. IPFS


Como puede suponer, le permite recuperar el contenido de un archivo en IPFS utilizando un hash múltiple. El tiempo de espera para recibir contenido es de 20 segundos.

 oraclize_query(“IPFS”, “QmTL5xNq9PPmwvM1RhxuhiYqoTJcmnaztMz6PQpGxmALkP”) 

Hello, Habr! (si el archivo con este contenido aún está disponible)

4. aleatorio


La generación de números aleatorios funciona de la misma manera que otras fuentes, pero si usa oraclize_query , se requiere una oraclize_query preparación de los argumentos. Para evitar esto, puede usar la oraclize_newRandomDSQuery(delay, nbytes, customGasLimit) auxiliar oraclize_newRandomDSQuery(delay, nbytes, customGasLimit) , configurando solo el retraso de ejecución (en segundos), el número de bytes generados y el límite de gas para llamar a __callback .
Usar random tiene un par de cosas a tener en cuenta:

  • Para confirmar que el número es realmente aleatorio, se utiliza un tipo especial de verificación: Ledger, que se puede realizar en la cadena de bloques (a diferencia de todos los demás, pero más sobre eso más adelante). Esto significa que en el constructor del contrato inteligente, debe establecer este método de verificación mediante la función:

     oraclize_setProof(proofType_Ledger); 

    Y al comienzo de la devolución de llamada debería haber un cheque en sí mismo:

      function __callback(bytes32 _queryId, string _result, bytes _proof) { require (oraclize_randomDS_proofVerify__returnCode(_queryId, _result, _proof) == 0) ); <...> 

    Esta verificación requiere una red real y no funcionará en ganache, por lo que para las pruebas locales puede eliminar temporalmente esta línea. Por cierto, el tercer argumento para __callback aquí es el parámetro opcional _proof . Siempre se requiere cuando se usa uno de los tipos de confirmación.
  • Si usa un número aleatorio para momentos críticos, por ejemplo, para determinar el ganador de la lotería, capture la entrada del usuario antes de enviar newRandomDSQuery. De lo contrario, puede ocurrir esta situación: oraclize llama a _callback y la transacción es visible para todos en la lista pendiente. Junto con esto, el número aleatorio en sí es visible. Si los usuarios pueden continuar, en términos generales, haciendo apuestas, entonces podrán especificar un precio de gasolina más alto y subir su tasa antes de ejecutar _callback, sabiendo de antemano que ganará.


5. cálculo


Esta es la más flexible de las fuentes. Le permite escribir sus propios scripts y usarlos como fuente de datos. La computación se lleva a cabo en AWS. Para la ejecución, debe describir el Dockerfile y juntarlo con archivos adicionales arbitrarios en un archivo zip, y descargar el archivo en IPFS. La implementación debe cumplir las siguientes condiciones:

  • Escriba la respuesta que desea devolver con la última línea en stdout
  • La respuesta no debe tener más de 2500 caracteres.
  • La inicialización y la ejecución no deben tomar más de 5 minutos en total.

Para un ejemplo de cómo se hace esto, consideraremos cómo realizar la unión más simple de las filas transmitidas y devolver el resultado.

Dockerfile:

 FROM ubuntu:16.04 MAINTAINER "info@rubyruby.ru" CMD echo "$ARG0 $ARG1 $ARG2 $ARG3" 

Variables de entorno ARG0 , ARG1 , etc. - Estos son los parámetros pasados ​​junto con la solicitud.
Agregue el dockerfile al archivo, inicie el servidor ipfs y agregue este archivo allí

 $ zip concatenation.zip Dockerfile $ ipfs daemon & $ ipfs add concatenation.zip QmWbnw4BBFDsh7yTXhZaTGQnPVCNY9ZDuPBoSwB9A4JNJD 

Utilizamos el hash resultante para enviar la solicitud a través de oraclize_query en el contrato inteligente:

 oraclize_query("computation", ["QmVAS9TNKGqV49WTEWv55aMCTNyfd4qcGFFfgyz7BYHLdD", "s1", "s2", "s3", "s4"]); 

Se usa una matriz como argumento, en el que el primer elemento es el multihash de archivo, y todo lo demás son los parámetros que se encuentran en las variables de entorno.

Si espera a que se complete la solicitud, __callback resultado s1 s2 s3 s4 .

Ayudantes de análisis y subconsultas


A partir de la respuesta devuelta por cualquier fuente, puede preseleccionar solo la información requerida utilizando una serie de ayudantes, como:

1. Analizador JSON


Viste este método en el primer ejemplo, donde solo se devolvió el precio del resultado devuelto por coinmarketcap:

 json(https://api.coinmarketcap.com/v1/ticker/ethereum/?convert=USD).0.price_usd 

El caso de uso es bastante obvio, volviendo por ejemplo:

 [ { "id": "ethereum", "name": "Ethereum", "symbol": "ETH", "rank": "2", "price_usd": "462.857", "price_btc": "0.0621573", "24h_volume_usd": "1993200000.0", "market_cap_usd": "46656433775.0", "available_supply": "100800968.0", "total_supply": "100800968.0", "max_supply": null, "percent_change_1h": "-0.5", "percent_change_24h": "-3.02", "percent_change_7d": "5.93", "last_updated": "1532064934" } ] 

Como se trata de una matriz, tomamos el elemento 0 y, a partir de él, el campo price_usd

2. XML


El uso es similar a JSON, por ejemplo:

 xml(https://informer.kovalut.ru/webmaster/getxml.php?kod=7701).Exchange_Rates.Central_Bank_RF.USD.New.Exch_Rate 

3. HTML


Puede analizar XHTML con XPath. Por ejemplo, obtenga una capitalización de mercado con etherscan:

 html(https://etherscan.io/).xpath(string(//*[contains(@href, '/stat/supply')]/font)) 

MARKET CAP OF $46.148 BillionB

4. Ayudante binario


Le permite cortar piezas de datos sin procesar utilizando la función de corte (desplazamiento, longitud). Es decir, por ejemplo, tenemos un archivo con el contenido de "abc":

 echo "abc" > example.bin 

Ponlo en IPFS:

 $ ipfs add example.bin added Qme4u9HfFqYUhH4i34ZFBKi1ZsW7z4MYHtLxScQGndhgKE 

Ahora corta 1 personaje del medio:

 binary(Qme4u9HfFqYUhH4i34ZFBKi1ZsW7z4MYHtLxScQGndhgKE).slice(1, 1) 

En la respuesta obtenemos b

Como habrás notado, en el caso del asistente binario, no se utilizó la fuente IP, sino IPFS. De hecho, los analizadores se pueden aplicar a cualquier fuente, digamos que no es necesario aplicar JSON a lo que devuelve la URL, puede agregar dicho contenido al archivo:

 { "one":"1", "two":"2" } 

Agréguelo a IPFS:

 $ ipfs add test.json added QmZinLwAq5fy4imz8ZNgupWeNFTneUqHjPiTPX9tuR7Vxp 

Y luego desmontar así:

 json(QmZinLwAq5fy4imz8ZNgupWeNFTneUqHjPiTPX9tuR7Vxp).one 

Obtenemos 1

Y un caso de uso particularmente interesante es combinar cualquier fuente de datos y cualquier analizador en una sola solicitud. Esto es posible utilizando una fuente de datos nested separada. Usamos el archivo que acabamos de crear en una solicitud más compleja (agregando valores en dos campos):

 [WolframAlpha] add ${[IPFS] json(QmZinLwAq5fy4imz8ZNgupWeNFTneUqHjPiTPX9tuR7Vxp).one} to ${[IPFS] json(QmZinLwAq5fy4imz8ZNgupWeNFTneUqHjPiTPX9tuR7Vxp).two} 

Obtenemos 3
La solicitud se forma de la siguiente manera: especifique la fuente de datos nested , luego, para cada solicitud, agregue el nombre de la fuente entre corchetes y, además, enmarque todas las subconsultas en ${..} .

Prueba


Oraclize proporciona un servicio útil de validación de consultas sin la necesidad de contratos inteligentes. Simplemente ingrese, elija una fuente de datos, un método de verificación y podrá ver que volverá a __callback si envía las solicitudes correspondientes

Para la verificación local junto con un contrato inteligente, puede usar una versión especial de Remix IDE que admita solicitudes de Oracle.

Y para verificar localmente con ganache, necesitará el puente ethereum , que implementará contratos inteligentes oraclize en su red de prueba. Para probar, primero agregue la siguiente línea al constructor de su contrato:

 OAR = OraclizeAddrResolverI(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475); 

correr

 ganache-cli 

Entonces

 node bridge --dev 

Espere hasta que los contratos estén muertos y pueda probar. En la salida del node bridge puede ver las solicitudes enviadas y las respuestas recibidas.

Otra ayuda no solo durante las pruebas, sino también en el uso real es la capacidad de monitorear las solicitudes aquí . Si está solicitando en una red pública, puede usar el hash de la transacción en la que se ejecuta la solicitud. Si usa autenticación, tenga en cuenta que se garantiza que se enviarán solo a mainnet, para otras redes puede devolver 0. Si la solicitud estaba en la red local, puede usar el id de solicitud, que devuelve oraclize_query . Por cierto, se recomienda mantener siempre esta identificación, por ejemplo, en una asignación similar:

 mapping(bytes32=>bool) validIds; 

En el momento de la solicitud, marque la identificación enviada como true :

 bytes32 queryId = oraclize_query(<...>); validIds[queryId] = true; 

Y luego, en __callback verifique que la solicitud con este ID aún no se haya procesado:

 function __callback(bytes32 myid, string result) { require(validIds[myid] != bytes32(0)); require(msg.sender == oraclize_cbAddress()); validIds[myid] = bytes32(0); <...> 

Esto es necesario porque __callback en una solicitud se puede llamar más de una vez debido a las peculiaridades de los mecanismos de Oraclize.

Autenticación


En la tabla con las fuentes, puede ver que diferentes fuentes pueden admitir diferentes tipos de confirmaciones, y se pueden cobrar diferentes tarifas. Esta es una parte muy importante de oraclize, pero una descripción detallada de estos mecanismos es un tema aparte.

El mecanismo más utilizado, al menos por nosotros, es TLSNotary con almacenamiento en IPFS. El almacenamiento en IPFS es más eficiente porque __callback no devuelve la evidencia en sí (tal vez en la región de 4-5 kilobytes), sino que es un hash múltiple mucho más pequeño. Para especificar este tipo, agregue una línea en el constructor:

 oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS); 

Solo podemos decir que este tipo, en términos generales, nos protege de la inexactitud de los datos recibidos de Oraclize. Pero Oraclize utiliza servidores de Amazon, que actúan como el auditor, por lo que solo tienen que confiar.

Lee más aquí .

Conclusión


Oraclize proporciona herramientas que aumentan significativamente el número de casos de uso para contratos inteligentes, así como IPFS, que se puede ver en varias versiones de consultas de Oracle. El problema principal es que nuevamente usamos datos externos que están sujetos a las amenazas de las que la cadena de bloques debería haber protegido: centralización, capacidades de bloqueo, cambios de código, suplantación de identidad. Pero si bien todo esto es inevitable, y la opción de obtener datos es muy útil y viable, solo debe saber por qué se introdujo el uso de blockchain en el proyecto y si el uso de fuentes externas no confiables reduce el beneficio a cero.

Si está interesado en algunos temas de desarrollo en Ethereum que aún no se han divulgado en estos artículos, escriba en los comentarios, tal vez los cubriremos a continuación.

Inmersión en desarrollo en Ethereum:
Parte 1: Introducción
Parte 2: Web3.js y gas
Parte 3: aplicación de usuario
Parte 4: despliegue y depuración en trufa, ganache, infura

Source: https://habr.com/ru/post/es417681/


All Articles