¿Cómo puede un contrato de Ethereum obtener datos de un sitio web?

¿Cuál es el proceso/flujo de trabajo detrás del proceso en el que un contrato de Ethereum obtiene algunos datos de un sitio web?

Respuestas (7)

Puedes usar un Oracle . Un oráculo es cualquier dispositivo o entidad que conecta datos del mundo real con la cadena de bloques.

Hay varios ejemplos de tecnologías Oracle. Chainlink y Provable (anteriormente Oraclize) son dos ejemplos que hacen exactamente eso.

Hay algunos ejemplos de código aquí , así como la documentación de nuestra Solidity API .

Oraclize está disponible tanto en la red principal como en la red de prueba, por lo que explorarlo debería ser fácil; sin embargo, si necesita ayuda, no dude en preguntar; incluso tenemos un canal gitter aquí .

Como puede ver , obtener datos de un sitio web es tan fácil como usar la función oraclize_query .

Nuestro buen comportamiento está garantizado por la prueba TLSNotary y se puede verificar fácilmente con este monitor de red del lado del cliente basado en la web .

Por ejemplo, para obtener el precio de ETHXBT del ticker de Kraken:

import "dev.oraclize.it/api.sol";

contract KrakenPriceTicker is usingOraclize {
    string public ETHXBT;

    function PriceTicker() {
        oraclize_setNetwork(networkID_testnet);
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
        oraclize_query("URL", "json(https://api.kraken.com/0/public/Ticker?pair=ETHXBT).result.XETHXXBT.c.0");
    }

    function __callback(bytes32 myid, string result, bytes proof) {
        if (msg.sender != oraclize_cbAddress()) throw;
        ETHXBT = result;
        // do something with ETHXBT
    }
} 
No tenía idea de que alguien ya estaba usando TLS Notary para esto. No hace falta decir - alucinante. Si está haciendo esto correctamente, debería permitir que la información no confiable se transmita a la red Ethereum. (Sin confianza siempre que confíe en el servidor).
¿Hay planes para abrir el código fuente de Oracle? (El software principal, no la interfaz).
Estamos planeando abrir muchas de las herramientas que estamos construyendo en Oraclize, pero no hay ningún plan para abrir nuestro motor interno.
¡Estoy lleno de preguntas sobre Oracle! Le pregunté a uno de ellos aquí .
¿Cuánto éter costaría una operación como esta?
@Rémi puede encontrar información sobre los precios en nuestra sección dedicada en docs.oraclize.it
¿Estoy en lo correcto en mis cálculos de que 1 llamada a través de Oracle ( docs.oraclize.it/#ethereum-custom-gas ) costará 200000*0.2337= 46000? Tomé el precio del gas de ethgasstation.info
¿Se puede usar en una cadena de bloques privada?

No puedes hacer esto directamente; Los contratos de Ethereum no pueden llegar a las URL, porque Ethereum necesita que todos puedan validar de forma independiente el resultado de ejecutar un contrato determinado, y no puede garantizar que todos obtengan el mismo resultado de una URL determinada.

Lo que puede hacer es conseguir que un tercero, o una combinación de terceros, acceda a la URL por usted y le diga lo que encontró. Pueden hacerlo firmando los datos para que su contrato pueda verificar la firma (esto es lo que hacemos en Reality Keys ) o enviando los datos de su contrato a su contrato (esto es lo que hace Oraclize).

Eche un vistazo a Etheropt en GitHub para ver un ejemplo de trabajo.

La desventaja de este enfoque es que los usuarios deben confiar en el servicio que accede a la URL. Si ese servicio está dañado o pirateado, esto dará como resultado SFYL. (Oraclize proporciona una prueba notarial de TLS de que los datos que proporcionaron eran los datos que le proporcionaron a usted realmente fueron proporcionados por la URL, pero eso realmente no ayuda con el riesgo de seguridad real; Detectar que le dieron la respuesta incorrecta es fácil, el el problema es que su contrato seguirá aceptando su respuesta aunque todos saben que están mintiendo...)

Hay algunos enfoques alternativos potenciales para este problema que no se basan en la reputación; El más famoso es Augur, que hace que los participantes del mercado voten sobre el resultado y tiene un sistema de incentivos que esperan que haga que la gente vote con sinceridad. También hay algunas propuestas interesantes entre los servicios totalmente confiables y la votación pura de los usuarios, como el "último oráculo" de Martin Koeppelmann .

Reality Keys parece exactamente lo que necesito. ¿Tiene algún ejemplo de contrato de solidez que llame a una de sus API gratuitas?
Aquí hay un ejemplo de código de Solidity: github.com/edmundedgar/realitykeys-examples-ethereum ...que alguien está convirtiendo en una aplicación web adecuada aquí: github.com/drupalnomad/runkeeper-oracle Un poco más de explicación aquí: realitykeys.com /desarrolladores/recursos No dude en ponerse en contacto si no puede resolverlo. La única API de deportes que tenemos en este momento es para resultados de fútbol, ​​pero es fácil agregar otras API si puede indicarnos algo con datos a los que desea que el proveedor de datos nos permita acceder.
¿Hay alguna manera de que un contrato inteligente de Solidity interactúe con las cuentas de Twitter o la API de Twitter para reconocer la cantidad de tweets "etiquetados de manera relevante" que hizo esa cuenta? Por ejemplo, se recompensa con una moneda ERC-20 a cada usuario de Twitter por twittear sobre "#EventA" X cantidad de veces.

Se ha escrito un tutorial detallado sobre cómo un contrato puede obtener datos para el cliente Python Ethereum (pyethapp):

https://github.com/ethereum/pyethapp/wiki/Creación-de-un-servicio-de-usuario:-Tutorial

Una de las características distintivas más poderosas de pyethapp es su capacidad para crear fácilmente servicios de usuario integrados: scripts escritos en python que se ejecutan junto con pyethapp y ejecutan código al inicio y cada vez que recibe un nuevo bloque. Esto le permite crear "demonios de servidor" para aplicaciones que requieren soporte automatizado periódico, como RANDAO, fuentes de datos, aplicaciones de "buzón descentralizado", despertadores, servicios de computación en la nube descentralizados, etc.

Pyethapp proporciona enlaces on_startque on_blockpermiten que el código se ejecute cuando se inicia y cuando procesa un bloque. Código de ejemplo del tutorial:

https://github.com/ethereum/pyethapp/blob/develop/examples/urlfetcher.py

import json, re
import random
import sys
import ethereum.blocks
import ethereum.utils
import ethereum.abi
import rlp
try:
    from urllib.request import build_opener 
except:
    from urllib2 import build_opener

my_privkey = ethereum.utils.sha3('qwufqhwiufyqwiugxqwqcwrqwrcqr')
my_address = ethereum.utils.privtoaddr(my_privkey).encode('hex')
print 'My address', my_address
# Address of the main proxy contract
my_contract_address = ethereum.utils.normalize_address('0xd53096b3cf64d4739bb774e0f055653e7f2cd710')

# Makes a request to a given URL (first arg) and optional params (second arg)
def make_request(*args):
    opener = build_opener()
    opener.addheaders = [('User-agent',
                          'Mozilla/5.0'+str(random.randrange(1000000)))]
    try: 
        return opener.open(*args).read().strip()
    except Exception as e:
        try:
            p = e.read().strip()
        except:
            p = e
        raise Exception(p)


true, false = True, False
# ContractTranslator object for the main proxy contract
ct = ethereum.abi.ContractTranslator([{"constant": false, "type": "function", "name": "get(string)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "string", "name": "url"}]}, {"inputs": [{"indexed": false, "type": "string", "name": "url"}, {"indexed": false, "type": "address", "name": "callback"}, {"indexed": false, "type": "uint256", "name": "responseId"}, {"indexed": false, "type": "uint256", "name": "fee"}], "type": "event", "name": "GetRequest(string,address,uint256,uint256)"}])
# ContractTranslator object for the contract that is used for testing the main contract
ct2 = ethereum.abi.ContractTranslator([{"constant": false, "type": "function", "name": "callback(bytes,uint256)", "outputs": [], "inputs": [{"type": "bytes", "name": "response"}, {"type": "uint256", "name": "responseId"}]}])

app, my_nonce, chainservice = None, None, None

# Called once on startup
def on_start(_app):
    print 'Starting URL translator service'
    global app, my_nonce, chainservice
    app = _app
    chainservice = app.services.chain
    my_nonce = chainservice.chain.head.get_nonce(my_address)


# Called every block
def on_block(blk):
    global my_nonce, chainservice
    for receipt in blk.get_receipts():
        for _log in receipt.logs:
            # Get all logs to the proxy contract address of the right type
            if _log.address == my_contract_address:
                log = ct.listen(_log)
                if log and log["_event_type"] == "GetRequest":
                    print 'fetching: ', log["url"]
                    # Fetch the response
                    try:
                        response = make_request(log["url"])
                    except:
                        response = ''
                    print 'response: ', response
                    # Create the response transaction
                    txdata = ct2.encode('callback', [response, log["responseId"]])
                    tx = ethereum.transactions.Transaction(my_nonce, 60 * 10**9, min(100000 + log["fee"] / (60 * 10**9), 2500000), log["callback"], 0, txdata).sign(my_privkey)
                    print 'txhash: ', tx.hash.encode('hex')
                    print 'tx: ', rlp.encode(tx).encode('hex')
                    # Increment the nonce so the next transaction is also valid
                    my_nonce += 1
                    # Send it
                    success = chainservice.add_transaction(tx, broadcast_only=True)
                    assert success
                    print 'sent tx'

El tutorial explica:

Esencialmente, una instancia de este servicio ejecutada por una parte confiable permite que los contratos de Ethereum accedan a cualquier fuente de datos que esté disponible a través de una API REST en Internet.

¿Hay alguna manera de que un contrato inteligente de Solidity interactúe con las cuentas de Twitter o la API de Twitter para reconocer la cantidad de tweets "etiquetados de manera relevante" que hizo esa cuenta? Por ejemplo, se recompensa con una moneda ERC-20 a cada usuario de Twitter por twittear sobre "#EventA" X cantidad de veces.
@ user610620 Veo que intentaste hacer esto como una pregunta que se cerró como un duplicado de esta. No estoy seguro de lo que puedo agregar: el contrato inteligente podría tener una función rewardTweeter(uint numberOfTweets, address tweeter). Un oráculo tendría que llamar a esa función.
¿Qué requisitos previos de hardware necesita un contrato inteligente de Ethereum para acceder a los datos de Twitter, y mucho menos buscar cosas en línea? una cuenta de Twitter habilitada para la API de Twitter? Si es así, ¿cómo invoca un contrato inteligente la API de Twitter para obtener datos?
@ user610620 Preguntar los requisitos previos de hardware tendría sentido para Oracle (servicio). Las respuestas aquí han tratado de explicar que los contratos no invocan las API: un oráculo invoca las API e invoca los contratos (posiblemente con datos que el oráculo recibió de las API).

Como han dicho otros, debes usar un Oracle. Algunos proyectos como ChainLink y Oracleize harán esto por usted.

Para configurar un oráculo por su cuenta, el proceso se ve así:

  1. Crear un contrato inteligente
  2. Configure un servidor que escuche algún evento externo, por ejemplo, un juego deportivo
  3. Una vez que se cumple una condición particular, por ejemplo, un equipo gana, su servidor enviará una transacción a su contrato Ethereum, actualizándolo con el resultado del evento.

Pruebe este ejemplo en Javascript (basado en Oracles en Ethereum ):

Contrato

pragma solidity ^0.4.17;

contract GamescoreOracle {
  address public owner;
  string public gameWinner;
  event CallbackGetGameWinner();

  constructor() public {
    owner = msg.sender;
  }

  function updateGameWinner() public {
    emit CallbackGetGameWinner();
  }

  function setWinningTeam(string teamname) public {
    require(msg.sender == owner, "Err: Not Authorized");
    gameWinner = teamname;
  }

  function getGameWinner() public view returns (string) {
    return gameWinner;
  }
}

servidor oracle

const fetch = require("fetch");
const OracleContract = require("./build/contracts/GamescoreOracle.json");
const contract = require("truffle-contract");

const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

const oracleContract = contract(OracleContract);
oracleContract.setProvider(web3.currentProvider);

web3.eth.getAccounts((err, accounts) => {
  oracleContract.deployed()
    .then((oracleInstance) => {
      // watch event, respond to event with callback
      oracleInstance.CallbackGetGameWinner()
        .watch((err, event) => {
          fetch.fetchUrl('http://www.mocky.io/v2/5baadacb3000002b00a68532', (err, m, response) => {
            const games = JSON.parse(response.toString());
            const winner = games.competitors.filter(team => team.isWinner)[0].team.nickname;

            // Send data back contract on-chain
            console.log(`Running contract.setWinningTeam(${winner})`)
            oracleInstance.setWinningTeam(winner, { from: accounts[0] })
          })
        })
    })
    .catch(err => console.log(err))
})
¿Hay alguna manera de que un contrato inteligente de Solidity interactúe con las cuentas de Twitter o la API de Twitter para reconocer la cantidad de tweets "etiquetados de manera relevante" que hizo esa cuenta? Por ejemplo, se recompensa con una moneda ERC-20 a cada usuario de Twitter por twittear sobre "#EventA" X cantidad de veces.

Un oráculo puede ser una cuenta humana controlada manualmente que alimenta datos de manera oportuna o mejor, un grupo automatizado de bots en servidores tradicionales que extraen un sitio web y alimentan datos a través de la cuenta. Necesitará codificar una dirección de contrato en su contrato como un oráculo de valores veraces para su programa. La clave privada se mantiene privada, a diferencia de un contrato inteligente, donde todos pueden verla. Es posible que se necesite alguna forma de deduplicar y formar consenso para una operación confiable completamente redundante.

Tienes que usar un oráculo .

Un oráculo es cualquier dispositivo que publica datos fuera de la cadena en una cadena de bloques.

Hay muchos servicios de Oracle que pueden ayudarlo a obtener datos fuera de la cadena dentro de la cadena dentro del marco de Ethereum. Es importante tener en cuenta que cada uno lo hace de manera muy diferente, lo que genera muchas consideraciones sobre por qué/cómo implementaría su tecnología. Estos son algunos de los más populares:

  1. Eslabón de la cadena
  2. Tellor
  3. Demostrable
  4. BandChain

Y aquí hay un ejemplo de cómo obtener datos a través de un nodo Chainlink :

pragma solidity ^0.6.0;

import "github.com/smartcontractkit/chainlink/evm-contracts/src/v0.6/ChainlinkClient.sol";

// MyContract inherits the ChainlinkClient contract to gain the
// functionality of creating Chainlink requests
contract ChainlinkExample is ChainlinkClient {
  // Stores the answer from the Chainlink oracle
  uint256 public currentPrice;
  address public owner;
  
    // The address of an oracle - you can find node addresses on https://market.link/search/nodes
  address ORACLE_ADDRESS = 0xB36d3709e22F7c708348E225b20b13eA546E6D9c;
  // The address of the http get job - you can find job IDs on https://market.link/search/jobs
  string constant JOBID = "628eded7db7f4f799dbf69538dec7ff2";
  // 17 0s = 0.1 LINK 
  // 18 0s = 1 LINK 
  uint256 constant private ORACLE_PAYMENT = 100000000000000000;

  constructor() public {
    setPublicChainlinkToken();
    owner = msg.sender;
  }

  // Creates a Chainlink request with the uint256 multiplier job
  // Ideally, you'd want to pass the oracle payment, address, and jobID as 
  function requestEthereumPrice() 
    public
    onlyOwner
  {
    // newRequest takes a JobID, a callback address, and callback function as input
    Chainlink.Request memory req = buildChainlinkRequest(stringToBytes32(JOBID), address(this), this.fulfill.selector);
    // Adds a URL with the key "get" to the request parameters
    req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD");
    // Uses input param (dot-delimited string) as the "path" in the request parameters
    req.add("path", "USD");
    // Adds an integer with the key "times" to the request parameters
    req.addInt("times", 100);
    // Sends the request with the amount of payment specified to the oracle
    sendChainlinkRequestTo(ORACLE_ADDRESS, req, ORACLE_PAYMENT);
  }

  // fulfill receives a uint256 data type
  function fulfill(bytes32 _requestId, uint256 _price)
    public
    // Use recordChainlinkFulfillment to ensure only the requesting oracle can fulfill
    recordChainlinkFulfillment(_requestId)
  {
    currentPrice = _price;
  }
  
  // withdrawLink allows the owner to withdraw any extra LINK on the contract
  function withdrawLink()
    public
    onlyOwner
  {
    LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
    require(link.transfer(msg.sender, link.balanceOf(address(this))), "Unable to transfer");
  }
  
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }
  
   // A helper funciton to make the string a bytes32
  function stringToBytes32(string memory source) private pure returns (bytes32 result) {
    bytes memory tempEmptyStringTest = bytes(source);
    if (tempEmptyStringTest.length == 0) {
      return 0x0;
    }
    assembly { // solhint-disable-line no-inline-assembly
      result := mload(add(source, 32))
    }
  }
}

Puede obtener datos de acciones, criptografía, ETF, etc. del contrato inteligente de OrFeed.org :

ejemplos:

divisas:

uint price = orfeed.getExchangeRate("JPY", "USD", "DEFAULT", 100000);

Valores:

uint price = orfeed.getExchangeRate("AAPL", "USD", "PROVIDER1", 1);

o en tiempo real desde un DEX:uint price = orfeed.getExchangeRate("BTC", "DAI", "SELL-UNISWAP-EXCHANGE", 100);