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

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

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

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

当前的协议倾向于使用预言机的概念来构建混合系统,这些系统依赖于智能合约和链下 API 来桥接 WEB 2.0 数据以及其他区块链。最著名的预言机是 Chainlink,它提供定价数据、与其他区块链的连接、对大多数 API 的访问以及各种其他数据馈送。

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

目前,像 Singularity NET 这样的组织正在努力构建预言机,以创建市场并轻松访问提供人工智能推理等服务的 API。

在本文中,目标是通过浏览当前用例的一般架构并使用 soliditynode.js 构建一个简单的链上天气预言机来对预言机概念进一步的了解。

事件驱动的预言机设计

image.png

在处理使用链下服务代表智能合约执行某些操作的问题时,要记住的最重要的事情是智能合约和服务之间没有正式的消息传递过程。有了这个前提条件,就知道智能合约不能 push ,服务必须是 listenwatchpull

该服务只有两个链上项目可以监视,即状态变量和事件。察状态变量很麻烦,因为它需要与合约进行多次交互。另一方面,事件不需要直接交互。

像这样发出智能合约事件:

emit newEvent(block.timestamp)

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

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

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

任务完成后,服务可以使用 web3 库与合约进行交易。典型的交易可以“上传”带有作业 ID 的请求/事件的结果,因此智能合约可以继续处理它计划对这些链下数据执行的任何操作。把这一切放在一起,它看起来像这样:

  1. Oracle 智能合约发出一个包含作业信息的事件。
  2. 链下预言机服务监听事件并在事件触发时拉取信息。
  3. 链下预言机与任何服务或数据交互以接收结果。
  4. 链下预言机与预言机智能合约进行交易以更新工作数据。
  5. 智能合约生态系统根据需要使用数据。

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

构建一个简单的预言机

为了更好的理解预言机的概念,本文选择了天气相关的服务,输入的数据容易传输,开放 API 也很丰富。鉴于对事件驱动预言机的理解,系统设计如下所示:

image.png

从系统设计来看,有一个 Solidity 合约和一个 node.js 程序,这个设计的要求很简单:

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

创建项目目录 node-oracle,进入项目目录,初始化项目:

npm init

安装依赖:

npm install hardhat chai @nomiclabs/hardhat-waffle @nomiclabs/hardhat-ethers ethers ethereum-waffle axios dotenv web3 --save-dev

现在编写智能合约,在项目根目录下创建智能合约文件夹并进入目录 contracts,运行以下命令来创建一个 truffle 项目,这样就可以开始写智能合约,其中写入以下代码:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract WeatherOracle {
    // 遍历 jobId => 检查智能合约交互的完成状态
    // 默认所有的为 false
    mapping(uint => bool) public jobStatus;
    // 如果jobStatus值为0,则表示结果实际上为0
    mapping(uint => uint) public jobResults;
    // 当前可用的 jobId 
    uint jobId;
    // 事件触发的预言机 API
    event NewJob(uint lat, uint lon, uint jobId);
    constructor(uint initialId){
        jobId = initialId;
    } 
    function getJobId() public view returns (uint) {
       return jobId;
    }
    function getWeather(uint lat, uint lon) public {
        emit NewJob(lat, lon, jobId);
        jobId++;
    }
    function updateWeather(uint temp, uint _jobId)public {
        jobResults[_jobId] = temp;
        jobStatus[_jobId] = true;
    }
}

已经逐行提供了包含更多细节的在线注释,智能合约是使用几个 map 来保存 jobIds(true = complete, false = incomplete) 工作的状态和结果。

该合约还提供了一个触发工作的函数,函数 getWeather 将位置数据作为参数,创建一新的 jobId,并发出一个带有相关位置和工作信息的事件。

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

测试合约

在部署之前,先来测试合约以确保可以逻辑都是正确的。在项目根目录中创建文件夹 test ,此文件夹可以包含客户端测试和以太坊测试。

test 文件夹中添加 test.js 文件,该文件将在一个文件中包含合约测试,代码如下:

const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("WeatherOracle", function () {
    let oracleContract;
    before(async () => {
        const oracleFactory = await ethers.getContractFactory("WeatherOracle");
        oracleContract = await oracleFactory.deploy(1);
        await oracleContract.deployed();
    });
    it("Should have currentJobId", async () => {
        let currentJobId = await oracleContract.getJobId();
        expect(currentJobId).to.equal(1);
    });
});

然后在项目根目录下执行脚本:

npx hardhat test

选择创建 Create an empty hardhat.config.js,替换为以下代码:

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
require("@nomiclabs/hardhat-waffle");
const config = {
    alchemy: "9aa3d95b3bc440fa88ea12eaa4456161", // 测试网络token
    privateKey:
        "e5275ae4ad0f4fd33e539e4d8af9fceaeb1c0f905f423cc6aa48bb6e", // 钱包私钥
};
module.exports = {
    solidity: "0.8.4",
    networks: {
        ropsten: {
            url: `https://ropsten.infura.io/v3/${config.alchemy}`,
            accounts: [config.privateKey],
            chainId: 3,
        },
    },
};

再次执行测试命令:

npx hardhat test

部署(到 Ropsten 测试网络)

在项目根目录下创建文件夹 scripts,然后在文件夹中创建文件 deploy.js

const main = async () => {
    const oracleFactory = await ethers.getContractFactory("WeatherOracle");
    const oracleContract = await oracleFactory.deploy(1);
    const [deployer] = await ethers.getSigners();
    console.log("Deploying contracts with the account: ", deployer.address);
    console.log("Account balance: ", (await deployer.getBalance()).toString());
    await oracleContract.deployed();
    console.log("Contract deployed to: ", oracleContract.address);
};
const runMain = async () => {
    try {
        await main();
        process.exit(0);
    } catch (error) {
        console.log(error);
        process.exit(1);
    }
};
runMain();

要部署合约,请在项目根目录下运行命令:

npx hardhat run scripts/deploy.js --network ropsten

执行完成之后可以看到结果:

Deploying contracts with the account:  0xDC13b48Cf2a42160f820A5Ad79B39E695C0c84
Account balance:  4807844068484
Contract deployed to:  0x0006544b9c915Ab3cb0e21000E4a4ABE746

node 服务

有了一个区块链预言机合约,可以在 node.js 中构建一个链下数据提供者,如下所示:

require("dotenv").config();
const fs = require("fs");
const axios = require("axios");
const Web3 = require("web3");
const web3 = new Web3(
    Web3.givenProvider ||
        "https://ropsten.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"
);
const contractAddress = "0x3753137066D0Dd20072fA1a359C532791893d326";
const contractAbi = JSON.parse(
    fs.readFileSync("../artifacts/contracts/oracle.sol/WeatherOracle.json")
).abi;
const contract = new web3.eth.Contract(contractAbi, contractAddress);
function callAPI(lat, long) {
    return axios
        .get(
            `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&appid=923ce2ef7d3a60a4fe0e7834d56beaaa`
        )
        .then((res) => {
            return res.data.main.temp;
        })
        .catch((err) => {
            return "Error";
        });
}
contract.events.NewJob(async (lat, lon, jobId) => {
    //use lat and lon to call API
    const temp = await callAPI(lat, lon);
    if (temp !== "Error") {
        // 如果接收到temp,发送数据到区块链上的updateWeather函数
        await contract.methods.updateWeather(temp, jobId).send();
    }
});
const run = async () => {
    const [lat, lon] = [39, 116];
    await contract.methods.getWeather(lat, lon);
};
run();

上面的 node.js 服务,设置了一个 web3 提供程序以使用网络正在使用的任何 URL 提供程序。请注意,交互钱包地址必须是运营商,必须确保 web3 提供商包含该帐户。

使用订阅的 web3 库来监听事件。在新事件上,代码使用内置 API 接口和事件参数来获取请求天气数据。

在该调用的结果上,web3库updateWeather通过合约 abi 调用该函数以将结果上传到合约。成功后,初始设计要求已全部满足,智能合约可以访问该工作的结果以执行其需要做的任何事情。

通过这样一个简单的示例,可以进一步了解构建简单的链下接口。

结论

区块链预言机将向 web 2.0 数据源开放 web 3.0 世界。这反过来体现了 web 3.0 更多的优势。事件驱动的预言机设计创建了一个将智能合约功能与传统软件工程技术相结合的框架。

随着这一概念的扩展,一个完整的生态系统可能会出现,考虑到与其他智能合约(未来的 API)交互的简单性,它允许最流畅的数据访问网络。

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


相关文章
|
5月前
|
供应链 监控 安全
构建未来:区块链技术在供应链管理中的应用
【8月更文挑战第30天】 在数字化浪潮的推动下,供应链管理正经历着前所未有的转型。本文深入探讨了区块链技术如何成为这一变革的核心动力。通过分析区块链的不可篡改性、透明度以及去中心化特性,揭示了其在提高供应链效率、确保商品真实性和加强各方信任方面的潜力。不同于常规摘要,本文还将提供具体的行业案例,以展现区块链技术在实际供应链管理中的应用场景与成效。
|
6月前
|
算法 分布式数据库 区块链
Python构建区块链
【7月更文挑战第10天】本文探讨了如何使用Python构建基本的区块链应用。区块链作为去中心化的分布式数据库,由包含交易数据的区块组成,通过哈希链接形成不可篡改的链。文中通过Python代码展示了如何创建`Block`类和`Blockchain`类,实现了区块的创建、哈希计算和链的构建。此外,还讨论了如何扩展区块链,包括添加智能合约、实现共识算法如Proof of Work、优化网络层以及引入隐私保护和跨链技术。
85 6
|
5月前
|
供应链 区块链 数据安全/隐私保护
构建未来的桥梁:区块链技术在现代金融中的应用
【8月更文挑战第16天】区块链技术,这一去中心化的数字账本技术,正逐渐渗透到金融领域的每一个角落。从提高交易透明度、降低操作成本到促进金融包容性,区块链正在重塑金融行业的未来。本文将探讨区块链技术如何在现代金融中发挥作用,以及它如何为金融服务的未来发展铺平道路。
|
7月前
|
安全 算法 定位技术
[Solidity][区块链安全入门]Solidity语言关于密码学知识的运用以及存在漏洞
密码学在区块链中扮演关键角色,确保机密性、完整性、身份认证和不可否认性。对称密钥加密用于快速加密,但不支持不可否认性。非对称加密(如RSA)解决了这一问题,每个用户拥有公钥和私钥。散列函数(如SHA-1、SHA-2)用于数字签名,保证信息来源和完整性。同态加密允许在不解密情况下处理加密数据,增强隐私保护。零知识证明则能验证信息正确性而不泄露额外信息,如ZCash使用该技术隐藏交易详情。环签名技术(如在门罗币中)隐藏签名者身份。区块链隐私保护措施包括混币技术,旨在混淆交易路径。网络和应用层面上也存在隐私挑战,需要综合策略来防御。
|
7月前
|
供应链 算法 Java
使用Java构建区块链应用
使用Java构建区块链应用
|
7月前
|
供应链 安全 区块链
区块链模块化:构建灵活、可扩展的未来网络
**区块链模块化**拆分系统为独立模块,提升**可扩展性**和**安全性**,增强**灵活性**,适应不同场景需求,如跨链互操作、行业定制和公共服务。模块化设计促进系统**定制化**,支持快速迭代,是区块链技术发展和创新的关键趋势。
|
7月前
|
供应链 安全 物联网
区块链跨链互操性:打破孤岛,构建互联互通的未来
**区块链跨链互操性摘要** - 跨链互操性是不同区块链间通信、交换数据和价值的能力,旨在打破区块链孤岛,提高扩展性、促进创新和增强安全性。 - 实现方式包括侧链(灵活性高,需解决安全性和性能问题)、原子交换(去中心化,但有跨链通信挑战)和中继链(通用解决方案,面临安全和性能挑战)。 - 挑战在于兼容性、安全性和性能,未来趋势将是标准化、安全隐私提升及跨链应用多样化,促进生态协同发展。跨链桥和协议如DLIP、ZKP将扮演关键角色。
|
7月前
|
供应链 安全 物联网
区块链跨链互操性:打破孤岛,构建互联互通的未来
**区块链跨链互操性摘要** - 跨链互操性是不同区块链间通信、交换数据和价值的能力,打破区块链“孤岛”现象。 - 提升扩展性、促进创新、增强安全性是其主要益处,通过侧链、原子交换、中继链等方式实现。 - 面临兼容性、安全性和性能挑战,未来将趋向标准化、提升安全隐私,并拓展多样化应用,促进区块链生态协同发展。
|
7月前
|
供应链 安全 区块链
区块链模块化:构建灵活、可扩展的未来网络
**区块链模块化**通过拆分系统为独立模块,如执行、结算、共识和数据层,提升**可扩展性**、**安全性和灵活性**。模块化允许定制化解决方案,适用于跨链互操作、行业特定需求及公共服务,如电子投票和版权保护。此方法降低耦合,增强安全性,为开发者创造更多创新机会,驱动区块链技术的未来发展方向。
|
8月前
|
机器学习/深度学习 供应链 区块链
深度学习在图像识别中的应用与挑战构建未来:区块链技术在供应链管理中的应用
【5月更文挑战第31天】 随着人工智能技术的飞速发展,深度学习已成为推动计算机视觉领域进步的关键力量。特别是在图像识别任务中,深度神经网络凭借其强大的特征提取和学习能力,显著提升了识别的准确性和效率。然而,随着应用的深入,深度学习在图像识别中也面临着数据偏差、模型泛化能力不足、计算资源消耗巨大等挑战。本文将探讨深度学习在图像识别领域的应用现状,分析其面临的主要技术挑战,并对未来发展趋势进行展望。