Basic principle of Chainlink Oracle

Keywords: Javascript Oracle Blockchain network Database

Basic principles of Chainlink
In this paper, we will briefly introduce the basic principle of Chainlink. If we use the shortest sentence to explain what Chainlink is, we can say that Chainlink is a decentralized Oracle project, so in order to understand the working principle of Chainlink, we should first understand what is a oracle.

Prophecy machine

Oracle's English name is Oracle, and Oracle (Oracle), a famous database service provider, has the same name, but they have nothing to do with it except for the same name. What's the meaning of oracle? Here's what I mean vocabulary.com Meaning of Oracle found on:

Back in ancient times, an Oracle was someone who offered advice or a prophecy thought to have come directly from a divine source. In modern usage, any good source of information can be called an oracle.

In ancient times, Oracle was a person who made suggestions or prophecies, and his suggestions or prophecies were believed to come directly from God. In modern usage, any good source of information can be called Oracle.

In this way, it is not difficult to understand that Oracle conveys the will of omnipotent and omniscient God, and oracle was originally used to predict good and bad times, and was also considered as an oracle at that time, conveying the meaning of God. Both Oracle and Oracle express the meaning of "information source".

The word "oracle" in the field of computer was first proposed by Turing. Based on Turing Machine, Turing added a black box called Oracle to form Oracle Machine. A prophet is an entity that can answer a specific set of questions. That is to say, it can input information to Turing Machine system to help Turing Machine complete the operation. The smart contract of Ethereum is "Turing Complete", which can be regarded as a Turing Machine in a sense. Therefore, the designers of Ethereum refer to this concept and call the information input to the Turing Complete smart contract as Oracle oracle. So the name "oracle" is not an original concept in the field of blockchain technology, it comes from very early computer abstract design, and there are similar concepts in cryptography and other fields.

In the field of blockchain, Oracle is considered to be a system that can provide external data sources for smart contracts. From the perspective of traditional technology architecture, Oracle is a middleware connecting the Smart Contract and the outside world of the blockchain, and an important infrastructure of the blockchain. Its role is to provide data information for the Smart Contract on the blockchain.

As defined by Ethereum, blockchain is a transaction based state machine. What it can do is very simple, that is, by submitting transactions to blockchain, it can transform blockchain from one state to another. In order to maintain a consensus, EVM's execution process must be fully determined and based only on the Ethereum state and the shared context of the signed transaction. This has two particularly important consequences: one is that EVM and smart contracts have no inherent source of randomness; the other is that external data can only be introduced as the data load of transactions. In popular terms, blockchain has no ability to obtain data actively. It can only use its own data. Due to the lack of data, the application scope of smart contract is very limited. At present, most of the applications are around token.

The certainty of blockchain means that on any node, as long as it is connected to the blockchain's distributed network, it can synchronize all historical blocks and play back a set of identical account books. In other words: without Internet connection, given a complete block, the node must be able to recreate the final state of the blockchain from scratch. If a ledger relies on the result of an external API call during its formation, the result of playback will be different in different time and different environment. This situation is not allowed by blockchain, so there is no network call at the beginning of blockchain design.

So what should we do to provide data to blockchain? Blockchain can only leave behind ledgers, while blockchain can only input transactions. We will start from these two aspects.

Almost every contract system has the function of event recording, such as the EventLog function in Ethereum.

Let's use an example to introduce the basic principle of the oracle. We establish a user contract on the Ethereum chain, which needs to obtain the temperature data of a city. Of course, the smart contract itself can't get the data information in the real world under the chain, which needs to be realized with the help of a prediction machine. The smart contract writes the city that needs to obtain the weather temperature into the EventLog. We will start a process under the chain, listen to and subscribe to the event log. After obtaining the request of the smart contract, we will specify the temperature of the city, call the backfill method in the contract by submitting the transaction, and submit it to the smart contract.

Declaration: the following code is only used to demonstrate the principle of Oracle, without parameter detection and error handling, please do not use it in the production environment.

Consumer contract:

contract WeatherOracle {
  // User stores weather value submitted by Oracle
  uint256 public temperature;

  // Define events
  event RequestTemperature (bytes city);

  // Issue a get request, i.e. issue an event log
  function requestTemperature (string memory _city) public {
    emit RequestTemperature(bytes(_city));
  }

  // The Oracle callback method is used to submit the data to the chain after the Oracle obtains the data
  function updateWeather (uint256 _temperature) public {
    temperature = _temperature;
  }
}

The above code is very simple. It defines a variable to store the result, a method to make a request, and a method to receive the result.

Under the chain, we start a process to get the log information by subscribing to the topic, and then build a transaction to submit a result to the contract.

func SubscribeEventLog() {
  topic := crypto.Keccak256([]byte("RequestTemperature(bytes)"))
  query := ethereum.FilterQuery{
    Topics: [][]common.Hash{
      {
        common.BytesToHash(topic),
      },
    },
  }

  // Log events to subscribe to related topics
  events := make(chan types.Log)
  sub, err := EthClient.SubscribeFilterLogs(ctx, query, events)

  // Load ABI file for contract
  ta, err := abi.JSON(strings.NewReader(AbiJsonStr))

  // Listening event subscription
  for {
    select {
    case err := <-sub.Err():
      log.Error(err)
      break
    case ev := <-events:
      // Get message for subscription
      ej, _ := ev.MarshalJSON()
      log.Info(string(ej))

      // Parse data
      var sampleEvent struct {
        City []byte
      }
      err = ta.Unpack(&sampleEvent, "RequestTemperature", ev.Data)
      log.Info(string(sampleEvent.City))

      // To build a transaction submission result, you need to provide a private key to sign the transaction
      CallContract("b7b502b...164b42c")
    }
  }
}
func CallContract(keyStr string) {
  addr := PrivateKeyToAddress(keyStr)
  nonce, err := EthClient.PendingNonceAt(ctx, addr)

  gasPrice, err := EthClient.SuggestGasPrice(ctx)

  privateKey, err := crypto.HexToECDSA(keyStr)

  auth := bind.NewKeyedTransactor(privateKey)
  auth.Nonce = big.NewInt(int64(nonce))
  auth.Value = big.NewInt(0)
  auth.GasLimit = uint64(300000)
  auth.GasPrice = gasPrice

  instance, err := event.NewEvent(common.HexToAddress("0x8A421906e9562AA1c71e5a32De1cf75161C5A463"), EthClient)

  // Call the updateWeather method in the contract to backfill the data "29"
  tx, err := instance.UpdateWeather(auth, big.NewInt(29))

  log.Info(tx.Hash().Hex())
}

Use a diagram to show the process:

Chainlink

Chainlink is a decentralized Oracle project. Its function is to provide blockchain with data generated in the real world in the safest way. Based on the realization of the basic Oracle principle, chainlink establishes a virtuous cycle ecosystem around LINK token through economic incentives. The chainlink Oracle needs to trigger through the transfer of LINK token.

LINK is the ERC677 contract on the Ethereum network. Please refer to this article for the differences between various ERC token s.

In the book "Mastering Ethereum", three design patterns of Oracle are proposed, namely

  • Immediate read
  • publish – subscribe
  • request – response

The Oracle function based on LINK ERC677 token belongs to the request / response mode. This is a more complex pattern. The figure above shows a simple request / corresponding process without aggregation process.

Let's take a requestEthereumPrice method in TestnetConsumer contract provided by Chainlink as an example to briefly describe the process of request response. This function is defined as follows:

function requestEthereumPrice(address _oracle, string _jobId)
  public
  onlyOwner
{
  Chainlink.Request memory req = buildChainlinkRequest(stringToBytes32(_jobId), this, this.fulfillEthereumPrice.selector);
  req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD");
  req.add("path", "USD");
  req.addInt("times", 100);
  sendChainlinkRequestTo(_oracle, req, ORACLE_PAYMENT);
}

Its function is to get the transaction price of ETH/USD from the specified API(cryptocompare). The parameters passed in by the function are the specified Oracle address and jobId. After the request parameters of some columns are set, the sendChainlinkRequestTo method is called to issue the request. Sendchainrequestto is an interface method defined in the library provided by Chainlink, as follows:

/**
  * @notice Create a request to the specified oracle address
  * @dev Create and store a request ID, increase the local nonce value, and use the 'transferAndCall' method to send the LINK,
  * Create request to target oracle contract address
  * Issue the ChainlinkRequested event
  * @param _oracle oracle address to send the request to
  * @param _req Completing the initialized Chainlink request
  * @param _payment Number of links requested to send
  * @return Request ID
  */
function sendChainlinkRequestTo(address _oracle, Chainlink.Request memory _req, uint256 _payment)
  internal
  returns (bytes32 requestId)
{
  requestId = keccak256(abi.encodePacked(this, requests));
  _req.nonce = requests;
  pendingRequests[requestId] = _oracle;
  emit ChainlinkRequested(requestId);
  require(link.transferAndCall(_oracle, _payment, encodeRequest(_req)), "unable to transferAndCall to oracle");
  requests += 1;

  return requestId;
}

Among them link.transferAndCall Method is the token transfer method defined by ERC677. Compared with ERC20's transfer method, it has one more data field, which can carry data while transferring. Here, the previously packaged request data is put in the data field and sent to the Oracle contract together with the transfer. The transferandcall method is defined as follows:

/**
  * @dev Transfer token with additional data to a contract address
  * @param _to Destination to
  * @param _value Transfer quantity
  * @param _data Additional data passed to the receiving contract
  */
  function transferAndCall(address _to, uint _value, bytes _data)
    public
    returns (bool success)
  {
    super.transfer(_to, _value);
    Transfer(msg.sender, _to, _value, _data);
    if (isContract(_to)) {
      contractFallback(_to, _value, _data);
    }
    return true;
  }

Transfer among them( msg.sender , to, _ value, _ Data) is to issue an event log:

event Transfer(address indexed from, address indexed to, uint value, bytes data);

Record the details of this transfer (sender, receiver, amount, data) in the log.

After receiving the transfer, the Oracle contract triggers the onTokenTransfer method, which checks the validity of the transfer and records more detailed data information by sending the OracleRequest event:

event OracleRequest(
  bytes32 indexed specId,
  address requester,
  bytes32 requestId,
  uint256 payment,
  address callbackAddr,
  bytes4 callbackFunctionId,
  uint256 cancelExpiration,
  uint256 dataVersion,
  bytes data
);

This log will be found in the Oracle contract log, as shown in the lower part of the figure. The nodes under the chain will subscribe to the logs of this topic. After obtaining the recorded log information, the nodes will parse the specific information of the request and obtain the result of the request through the network API call. After that, the fulloraclerequest method in the Oracle contract is called to submit the data to the chain by submitting the transaction. Fulloraclerequest is defined as follows:

/**
  * @notice Called by Chainlink node to complete the request
  * @dev The submitted parameter must be the hash parameter recorded by the 'oracleRequest' method
  * The callback function of the callback address will be called, and no error will be reported when 'require' is checked, so that the node can get reward
  * @param _requestId Request ID must match requester
  * @param _payment Issue payment amount to Oracle in wei
  * @param _callbackAddress Callback address of the completion method
  * @param _callbackFunctionId Callback function to complete method
  * @param _expiration The requester can cancel the expiration time before the node should respond
  * @param _data Data returned to consumer contracts
  * @return Status value of external call success
  */
function fulfillOracleRequest(
  bytes32 _requestId,
  uint256 _payment,
  address _callbackAddress,
  bytes4 _callbackFunctionId,
  uint256 _expiration,
  bytes32 _data
)
  external
  onlyAuthorizedNode
  isValidRequest(_requestId)
  returns (bool)
{
  bytes32 paramsHash = keccak256(
    abi.encodePacked(
      _payment,
      _callbackAddress,
      _callbackFunctionId,
      _expiration
    )
  );
  require(commitments[_requestId] == paramsHash, "Params do not match request ID");
  withdrawableTokens = withdrawableTokens.add(_payment);
  delete commitments[_requestId];
  require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas");
  // All updates to the oracle's fulfillment should come before calling the
  // callback(addr+functionId) as it is untrusted.
  // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern
  return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solhint-disable-line avoid-low-level-calls
}

After a series of tests, this method will return the result to the consumer contract through the previously recorded callback address and callback function:

_callbackAddress.call(_callbackFunctionId, _requestId, _data);

Such a request is completed.

summary

Starting from the concept of Oracle, this paper explains the basic process of the request / response model of Chainlink Oracle through a simple example of obtaining ETH price, hoping to help you understand the operation principle of the oracle and Chainlink.

reference resources:
https://medium.com/@liyunlong518/%E5%88%9B%E5%BB%BA%E4%BD%A0%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E4%BB%A5%E5%A4%AA%E5%9D%8A%E9%A2%84%E8%A8%80%E6%9C%BA-df53e50cc2d5

Welcome to the Chainlink developer community

Posted by jeethau on Thu, 28 May 2020 02:43:42 -0700