使用 Solidity 和 Node.js 构建简单的区块链预言机

区块链上的预言机是允许区块链世界与来自网络其余部分的数据交互的框架,我们将其称为网络 2.0 世界。随着智能合约的应用不断扩大,处理独特用例所需的各种数据也将不断扩大。

1.jpg

区块链上的预言机是允许区块链世界与来自网络其余部分的数据交互的框架,我们将其称为网络 2.0 世界。随着智能合约的应用不断扩大,处理独特用例所需的各种数据也将不断扩大。

事实上,web 2.0和web 3.0是两个截然不同的网络,目前最有用的数据都存在于web 2.0中。通过创建一组协议使智能合约能够访问这些数据,新一代的web、系统设计和区块链将出现。

目前的协议已经借鉴了预言机的概念,利用智能合约和链下api来构建混合系统,将web 2.0数据和其他区块链连接起来。最著名的预言机是Chainlink,它提供定价数据、与其他区块链的连接、对大多数api的访问以及各种其他数据馈送的服务。

其他主要的例子包括代币桥,它允许在链下服务的帮助下在链之间移动代币和数据。随着时间的推移,可能会出现更多独特的预言机。

事件驱动的预言机设计

2.jpg

在解决使用链下服务代表智能合约执行某些操作的问题时,最重要的是要记住,在智能合约和服务之间没有正式的消息传递过程。根据这个假设,我们知道智能合约不能“推送”,所以服务必须可听、可看。

只有两个链上项目,服务才有能力“监听”,就是状态变量和事件。观察状态变量很麻烦,因为它需要与合约进行多次交互。另一方面,事件不需要直接交互。

智能合约事件的触发方式如下:

emit newEvent(block.timestamp)

事件可以被看作是由开发人员定义的智能合约的操作日志。与其他类型的日志一样,其他服务也可以订阅此提要,以“监听”特定类型的事件,从它们的参数中收集数据,并对这些事件做任何它们想做的事情。这些日志对任何访问区块链的人都是可见的,并且可以通过像web3.js这样的库进行访问。

有了这种独特的通信系统,智能合约可以“通知”外部世界的服务事件,或需要完成的预言机案例工作。理解事件和链上到链下的消息传递是预言机设计中最重要的部分。

一旦服务发现了一个新的事件,并触发了它的操作,它就可以获取事件中的有价值的数据和惟一的jobID,并像其他任何程序一样执行链下操作。

任务完成后,服务可以使用 web3 库与合约进行交易。典型的交易可以“上传”带有jobID 的请求/事件的结果,这样智能合约就可以继续使用链下数据进行任何已计划好的工作。把所有这些放在一起,就会如下所示:

  1. 预言机智能合约会发出一个带有job信息的事件。
  2. 链下预言机服务“监听”事件并在事件被触发时提取信息。
  3. 链下预言机与任何服务或数据进行交互以接收结果。
  4. 链下预言机与预言机智能合约进行交易,以更新job数据。
  5. 智能合约生态系统会根据需要使用数据。

当然,这是预言机设计的过度简化,如Chainlink,它包括许多节点和共识协议,以确保预言机数据是去中心化的。虽然有趣且重要,但简单的理解将是构建概念的最佳背景。

构建一个简单的预言机

为了可视化预言机的概念,我们可以构建一个简单的单服务、单节点预言机,使这个概念不那么令人生畏。

我选择了像天气这样的服务,因为输入数据很容易传输,而且api很丰富。考虑到我们对事件驱动的预言机的理解,我们的系统设计将看起来像这样:

3.jpg

气象预言机系统图

正如所看到的,我们有一个solidity合约和一个node.js程序,因此这个设计的要求很简单:

  • Solidity 智能合约
  • node. js
  • Web3. js
  • 天气API (使用OpenWeather)

首先,我们可以开始智能合约:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

contract WeatherOracle {
    //mapping from jobId => completion status for smart contract interactions to check;
    //default false for all + non-existent
    mapping(uint => bool) public jobStatus;

    //mapping jobId => temp result. Defaultfor no result is 0.
    //A true jobStatus with a 0 job value implies the result is actually 0
    mapping(uint => uint) public jobResults;

    //current jobId available
    uint jobId;

    //event to trigger Oracle API
    event NewJob(uint lat, uint lon, uint jobId);

    constructor(uint initialId){
        jobId = initialId;
    } 

    function getWeather(uint lat, uint lon) public {
        //emit event to API with data and JobId
        emit NewJob(lat, lon, jobId);
        //increment jobId for next job/function call
        jobId++;
    }

    function updateWeather(uint temp, uint _jobId)public {
        //when update weather is called by node.js upon API results, data is updated
        jobResults[_jobId] = temp;
        jobStatus[_jobId] = true;

        //Users can now check status and result via automatic view function
        //for public vars like these mappings
    }
}

我已经逐行提供了更详细的注释,但是为了总结我们的合约,我们使用了一对映射来保存jobIDs(true = complete, false = incomplete)的状态和作业的结果。

合约提供了一个用于触发job的函数getWeather,该函数以位置数据作为参数,创建jobID,并发出带有相关位置和job信息的事件。

此预言机的一个重要特性是updateWeather函数上的操作符修饰符。为了只允许服务与这个函数交互,需要这个修饰符,否则任何人都可以更新job数据。

现在我们有了一个区块链预言机合约,可以在 node.js 中构建一个链下数据提供者;

require('dotenv').config();
const fs = require("fs");
const https = require('axios');
const Web3 = require('web3');

//intialize Web3 with the Url of our environment as a variable
const web3 = Web3(new Web3.providers.HttpProvider(process.env.RPC));
//get contract address and abi file path from env vars
const contractAddress = process.env.CON_ADDR;
const contractAbi = JSON.parse(fs.readFileSync(process.env.ABI)).abi;
//initialize contract variable
var contract =  new Web3.eth.Contract(contractAbi, contractAddress);

//simple function for calling API with lat and long, returning temp from JSON(see included doc link in article)
function callAPI(lat, long){
    axios.get(`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&appid=${process.env.API_KEY}`)
        .then(res => {
            return res.data.main.temp;
  })
  .catch(err => {
    return "ERROR"
  });
}

//While loop until program is canceled to continue to receive events
while(true){
    //initialize a contract listener for emmisions of the "NewJob" event, see web3.js for docs
    contract.on("NewJob", (lat, lon, jobId) => {
        //use lat and lon to call API
        var temp = callAPI(lat, lon);
        if(temp != "ERROR"){
            //send data to updateWeather function on blockchain if temp is received
            await contract.methods.updateWeather(temp, jobId).send();
        }
    })
}

交互的钱包地址必须是一个运营商,我们必须确保我们的web3提供商包含该帐户。

使用我们订阅的web3库来“监听”事件。对于新事件来说,代码使用内置API接口和事件参数来获取我们请求的天气数据。

对于调用的结果,web3库通过合约abi调用updateWeather函数,将结果上传到合约中。一旦成功,初始设计需求就全部满足了,智能合约就可以访问该工作的结果,完成它需要做的任何事情。

虽然这是一个简单的示例,但它可能会让我们了解如何为独特的用例构建简单的链下接口。

结论

总之,区块链的预言机将为web 3.0世界打开web 2.0数据源的大门。这反过来又允许web 3.0开始实现更多的优势。事件驱动的预言机设计创建了一个将智能合约功能与传统软件工程技术相结合的框架。

随着这一概念的扩展,可能会出现一个完整的生态系统,可以实现最流畅的数据接入网络。

为这个生态系统做出贡献可能有很大的机会,从一个像天气这样简单的应用程序开始,是深入探索和发现新事物的第一步。

Source:https://medium.com/better-programming/building-a-simple-blockchain-oracle-with-solidity-and-node-js-29eacdad31f1

关于

ChinaDeFi - ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。

本文首发于:https://mp.weixin.qq.com/s/htYgYMmXgJw4hMr7hDEx4w

点赞 1
收藏 6
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
ChinaDeFi 去中心化金融社区
ChinaDeFi 去中心化金融社区
ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。