作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
约翰R. 科辛斯基的头像

By 约翰R. 辛斯

做了近二十年的全栈开发, 约翰研究过物联网, 区块链, 网络, 以及使用C/ c++的移动项目, .. NET, SQL和JS.

专业知识

以前在

摩根士丹利(Morgan Stanley)
分享

注意:本指南出于历史目的保留在这里,但是 松露 到2023年9月日落的时间.

Ethereum Smart Contracts are more than just “the new hot thing.“我相信,在即将到来的互联网新时代,它们(或相关的东西)将改变人类彼此做生意的方式. 时间会证明情况是否如此.

这是三部分文章的第一部分 以太坊智能合约开发 与可靠性, 最具体的是探索所谓的“神谕”合约的使用,这些合约基本上是将数据注入区块链以供其他智能合约使用的合约.

  • 第1部分:介绍使用松露进行开发,以及进一步实验的项目设置
  • 第2部分:深入研究代码以进行更深入的研究
  • Part 3: A conceptual discussion of oracles with smart contracts

这样做的目的是, 本系列的第1部分, 不是要深入了解oracle契约的概念吗, 它们背后的哲学, or even very deeply 成 what they are; the goal of this part of our Ethereum oracle tutorial is simply to:

  • 让你用松露建立智能合约.
  • Build a smart contract project which will serve us in parts 2 和 3.
  • 介绍一些与以太坊智能合约和智能合约编码相关的概念.
  • Introduce the compile/run/debug cycle with 松露 和 smart contracts.

定义:甲骨文. 一种智能合约从区块链外部访问数据的方法. 一种智能合约, 甲骨文从外部世界获取数据,并将其放入区块链中,供其他智能合约使用.

本文的第一部分将包括所有先决条件的设置. Then, we’ll set up a single Ethereum contract 和 测试 it with 松露. Finally, we’ll separate the oracle 从 the client 和 测试 them jointly.

软件需求

  • 任何主流操作系统都可以工作, 当然,在不同的系统上,有些安装和设置会有所不同. 我在Ubuntu Linux(16)上完成了所有这些.04). I have also had no problems setting up the environment on Windows. 我没有尝试过Mac,尽管我知道在Mac上这样做很常见.
  • It is not necessary to run a full eth node; we will use 松露, which comes with its own 测试net. 如果你知道你在做什么, you 可以 use any other 测试net of your choosing; 松露’s local dev 测试net is just the easiest 和 most accessible for purposes of this tutorial.

知识的需求

  • 区块链工作原理的基本知识
  • 理解什么是基于区块链的智能合约
  • 一些基本的智能合约开发经验将会有所帮助, 但如果你聪明又有野心,就没必要了. 我知道你是!)

本系列文章 可以 作为智能合约的第一个介绍, 但它很快就会发展成更高级的概念. 如果这是您的第一个eth智能合约教程,请准备好快速攀升. 如果你感到自信, great; if not, 你可以先获得一两个更简单的“hello world”类型的教程. 看看 以太坊的一篇或以前的文章Cryptozombies对于初学者来说.

Caveat: The smart contract space, being so new, changes quickly. 当您阅读本文时,撰写本文时新的坚实语法特性可能已经被弃用或过时了. Geth版本可能来来去去. 可靠性 is always adding new language features 和 deprecating old ones. 许多新功能目前正在开发中. So, be prepared if necessary to adapt the information in this article to the new l和scape of the future; if you’re serious ab出 learning smart contract development, 那我就对你有信心.

应用示例描述

用例:用户在拳击比赛中下注.

  • 用户可以拉出可下注的拳击比赛列表.
  • 用户可以选择一场比赛,并对获胜者下注.
  • 用户可以投注超过指定最低金额的任何金额.
  • If the user’s pick loses, the user loses the entire amount of the bet.
  • 如果用户的选择获胜, 用户根据他/她下注的大小和对比赛输家下注的总金额获得一部分赌注, 房子(合同所有人)获得一小部分奖金.

什么是以太坊甲骨文?

Smart contracts are still kind of a new thing; they’ve yet to take the mainstream, 而且它们将如何运作的许多方面还没有被敲定和标准化. I will briefly explain the impetus behind the idea of the “oracle”—和 be patient; we’ll get 成 it in more depth in later parts.

设计区块链合约 难道不像编程客户机-服务器应用程序吗. One important difference is that data with which the contract interacts, 一定已经在区块链上了. 没有召唤 区块链的. 它不仅不被语言支持,也不被区块链范式支持. The contract 可以 take bets in the form of Ethereum-based currency, 将它们存储在合同中, 和 release them to the correct wallet addresses according to a formula, 当宣布比赛的获胜者时. 但是合同怎么知道谁是赢家呢? 它不能查询REST API或类似的东西. 它只能使用已经在区块链中的数据! 智能合约的许多用例都遇到了类似的问题——除非它们能够与区块链之外的世界进行交互,否则它们会受到严重的限制.

如果合约只能与区块链上的数据交互, an obvious 索尔ution is to inject the necessary data 成 the blockchain. 这就是神谕. 神谕是另一种契约, 谁将数据注入区块链, 允许其他合约使用它. 虽然这可能会引发信任和不信任的问题, 现在就接受这就是神谕吧. 在本系列的第3部分中,我们将讨论这些细微差别. 在我们的示例用例中, the oracle will be the contract that injects data 成 the blockchain, regarding (a) what matches are available 和 (b) who won those matches, 一旦决定.

搭建以太坊开发环境

对于基本设置,我们将安装:

  • Geth(目前可选)
  • 松露
  • 巧克力酱CLI(可选)
  • 开发环境(可选)

本文篇幅有限,不能作为环境设置的完整指南,只能作为一个粗略的指南. 没关系, 虽然, 因为对于你的特定操作系统,已经有很多更完整的安装指南了,而且互联网真的不需要新的. 因此,我将带您快速了解这条路径,并根据需要为您提供一些资源,以获取更多细节. 根据您的系统要求和Google的指示,准备好安装要求和先决条件.

甲骨文合同流程说明

安装Geth(可选)

Geth是go -以太坊, the Ethereum core software; while it’s not necessary for this exercise at all, 任何想要成为以太坊开发者的人都应该拥有它并熟悉它. 如果您打算将智能合约部署到实时以太坊网络,这将是必要的.

安装松露

松露是我们用于开发的主要原料, 绝对是本指南的要求.

安装巧克力酱CLI(可选)

我建议安装 巧克力酱CLI 作为另一个测试工具使用,尽管我们不会在教程中实际使用它. 它是可选的.

以太坊开发环境

使用任何简单的文本编辑器都不可能完成整个教程, 像记事本+ +, 中用户, vi, 或您选择的任何文本编辑器或IDE. I personally am using Visual Studio Code with the following extensions:

  • 可靠性
  • 稳健扩展
  • 材质图标主题

注意:扩展不是必需的—它们只是为了更好的编码环境.

设置代码

项目设置

松露 is a very convenient tool for compiling smart contracts, 将它们迁移到区块链, 它还提供了开发和调试实用程序. Some project setup will be necessary in order to integrate with 松露. 现在我们将在松露和目录结构中为我们的项目设置shell. Just sit back, follow the steps robotically for now, 和 enjoy.

Create a directory to house all the code; call it oracle-example.

在根目录中, 创建两个子目录, 因为最终, 该项目将包括两个子项目. 创建目录:

  • / oracle-example /客户端
  • / oracle-example甲骨文

进入客户端文件夹,因为这是我们要开发的第一个项目. 打开终端(命令行)窗口 / oracle-example /客户端 文件夹.

运行命令 松露init.

注意,在创建的许多文件中有 松露-config.js松露.js. 我们不需要两个,所以删掉 松露-config.js (只是为了避免混淆和混乱).

我们需要编辑 松露.js, in order to point 松露 in the right direction for 测试ing. 替换的内容 松露.js 下面是:

    模块.出口= {
        网络:{
            发展:{
                主持人:“localhost”,
                端口:8545
                network_id: "*" //匹配任意网络id
            }
        }
    };

httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step1/client/Truffle.js

注意,松露 init创建了一个名为 迁移 (oracle-example /客户/迁移). 在该文件夹中应该有一个名为 1 _initial_migration.js.

在迁移目录中添加另一个文件并命名它 2 _deploy_contracts.js,内容如下:

    var BoxingBets =工件.要求(“BoxingBets”);

    模块.导出=函数(部署器){
        部署人员.部署(BoxingBets);
    };

httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step1

添加代码

现在 that the simple setup is 出 of the way, we’re set to begin coding. 还记得, 本文的这一部分仍然是介绍和设置, 我们很快地看一下代码. We’ll get 成 more in-depth explanations of the code in part 2, 和 more in-depth discussion of the architecture 和 concept in part 3. 也就是说,我们将快速触及代码中一些明显的核心概念. 小心跟随以跟上.

The full code for this step in the process is available on GitHub: httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step1

稳固性契约

可靠性中的“契约”大致类似于其他面向对象语言中的类. 该语言本身已被拿来与Golang和JavaScript等进行比较. 索尔idworks中的其他一些语言结构(我们将在后面举例)是修饰语, 库, 和接口. Inheritance (including multiple inheritance) is supported for contracts. 坚实合同文件有一个 .索尔扩展.

甲骨文接口

将这个文件添加到你的项目中: / oracle-example /客户/合同/ 甲骨文Interface.索尔

httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step1/client/contracts/OracleInterface.sol

Normally, the oracle interface would be just that—an interface. 对于第一次迭代, it’s just a simple class contained within the 可靠性 project, 现在只是一个占位符. 我们将在下一步中将其移出, 在我们成功编译并在松露上运行契约之后. 稍后将其转换为实际接口后,函数实现将为空.

客户合同

将这个文件添加到你的项目中: / oracle-example /客户/合同/ BoxingBets.索尔

httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step1/client/contracts/BoxingBets.sol

这是消耗拳击比赛数据的合同, 允许用户查询可用的匹配项, 并在他们身上下注. 在以后的迭代中,它将计算并支付奖金.

编译和运行

现在 is when we’ll see if we got everything right the first time!

编译和迁移合同

打开终端 / oracle-example /客户/ 文件夹

用下面的命令编译代码:

松露编译

备用方案:使用我的recompile.Sh shell脚本(httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step1/client/recompile.sh).

请注意,您将看到许多警告,因为我们的代码尚未形成最终形式!

打开松露开发控制台:

松露开发

现在, in the 松露 developer con索尔e, migrate to the 测试 network:

松露(develop)> migrate

运行合同

At the development con索尔e prompt, enter the following line of code:

松露(develop)> BoxingBets.部署().then(inst => { instance = inst })

现在, “instance”是指向BoxingBets合约的变量,可以用来调用它的公共方法.

使用以下命令测试它:

松露(develop)> instance.测试(3、4) 

中包含了一个公共“测试”函数 BoxingBets.索尔. 它会将你传递给它的两个数字相加, 只是为了证明合约正在执行代码, 我们可以从松露开发控制台调用它. 如果我们得到一个看起来很正常的回复(见下文),那么我们的工作就完成了(至少现在是这样)。.

分离以太坊甲骨文

如果到目前为止一切都很成功,那么我们就渡过了难关. 接下来我们要做的是将oracle合同从BoxingBets合同中分离出来. 在实际使用中, oracle的合约将与区块链上的客户端合约分开存在, 所以我们需要能够:

以太坊oracle合约流程示意图

  • 通过区块链地址实例化它.
  • 动态修改客户端契约用来引用oracle的oracle地址.

简而言之, 我们现在要做的是将oracle和客户端分离为两个独立的区块链合约实体, 让他们互相交谈. 客户端将通过地址实例化oracle并调用它.

客户合同

第一个, 我们将修改客户端契约(client),使其指向oracle的动态接口,而不是具体的类. 然后我们将确保它从外部契约实例化oracle.

进入 / oracle-example /客户/合同/ 甲骨文Interface.索尔. 正如我们之前提到的,这目前还不是一个接口,但我们即将使它成为一个接口. 将其中的内容替换为:

httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step2/client/contracts/OracleInterface.sol

实用可靠度^0.4.17;

契约甲骨文Interface

    enum MatchOutcome {
        Pending, //match还没有被决定
        正在进行,//比赛已经开始 & 正在进行中
        平局,//除明显的赢家以外的任何东西(例如.g. 取消)
        决定//获胜者的参与者索引 
    }

    getPendingMatches()公共视图返回(bytes32[]);

    函数getAllMatches ()公共视图返回(bytes32[]);

    function matchExists(bytes32 _matchId) public view returns (bool); 

    getMatch(bytes32 _matchId)
        bytes32 id,
        字符串的名字, 
        字符串的参与者,
        uint8 participantCount,
        使用uint日期, 
        MatchOutcome结果, 
        int8赢家);

    function getMostRecentMatch(bool _pending) public view returns (
        bytes32 id,
        字符串的名字, 
        字符串的参与者,
        使用uint participantCount,
        使用uint日期, 
        MatchOutcome结果, 
        int8赢家);

    函数测试Connection () public pure returns (bool);

    函数addTestData () public; 
}

In BoxingBets.索尔,我们将替换这一行:

   甲骨文Interface internal boxing甲骨文 = new 甲骨文Interface(); 

用这两行:

   oracleaddr = 0;
    甲骨文Interface internal boxing甲骨文 = 甲骨文Interface(boxing甲骨文Addr); 

现在我们需要的是一种设置oracle地址的方法, 动态, 和 a function that we 可以 call to find 出 the current oracle address. 将这两个函数添加到 BoxingBets.索尔:

   /// @notice sets the address of the boxing oracle contract to use 
    /// @dev设置错误的地址可能会导致错误的返回值或错误 
    /// @param _oracleAddress打包oracle的地址 
    /// @return 真正的 if connection to the new oracle address was successful
    函数set甲骨文Address(地址_oracleAddress)外部onlyOwner返回(bool) {
        boxing甲骨文Addr = _oracleAddress;
        boxing甲骨文 = 甲骨文Interface(boxing甲骨文Addr); 
        返回boxing甲骨文.测试Connection ();
    }

    /// @notice获取正在使用的装箱oracle的地址 
    /// @返回当前设置的oracle地址 
    function get甲骨文Address() external view returns (address) {
        返回boxing甲骨文Addr;
    }

最后,为了测试客户端和oracle之间的连接,我们可以替换 测试 function in BoxingBets with a function to 测试 the oracle connection:

   /// @notice for 测试ing; 测试s that the boxing oracle is callable 
    ///如果连接成功,返回真正的 
    函数测试甲骨文Connection ()公共视图返回(bool) {
        返回boxing甲骨文.测试Connection (); 
    }

Ownable

注意,的定义 set甲骨文Address 有一个 onlyOwner 后面跟着修饰语. 这限制了该函数不能被合约所有者以外的任何人调用, 即使函数是公共的. 这不是一种语言特性. 这是所有权合同提供给我们的, 它是从OpenZeppelin的通用实用程序可靠性合约库中提取出来的. 我们将在第2部分详细介绍,但为了便于使用 onlyOwner 修饰语,我们需要做一些改变:

复制 Ownable.索尔httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step2/client/contracts/Ownable.sol/ / oracle-example /客户/合同.

在顶部添加对它的引用 BoxingBets.索尔,像这样:

进口”./ Ownable.索尔”;

(您可以将它添加到导入的行下面 甲骨文Interface.索尔.)

修改BoxingBets的合同声明,使其继承Ownable,如下所示:

合同{

:

合同BoxingBets是可拥有的{

我们应该都准备好了. 完整的代码在这里,以防你迷路了: httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step2/client/contracts

甲骨文公司的合同

设置

现在,BoxingBets合约试图通过地址引用一个完全独立的合约(即oracle), 我们的下一项工作是创建oracle合同. 现在我们要创建一个包含oracle契约的独立项目. It’s essentially the same setup that we’ve already done for the client contract project; that is, 设置松露进行编译和开发.

您应该已经有一个名为 / oracle-example / oracle / 这是我们在上一步中创建的目录(如果没有,现在就创建空目录). 在该目录下打开终端.

  • 运行命令 松露init.
  • 删除 / oracle-example / oracle / 松露-config.js.
  • 编辑 / oracle-example / oracle /松露.js 像这样:
    模块.出口= {
        网络:{
            发展:{
                主持人:“localhost”,
                端口:8545
                network_id: "*" //匹配任意网络id
            }
        }
    };

请看下面的例子: httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/Truffle.js

内部 / oracle-example / oracle /迁移,创建一个名为 2 _deploy_contracts.js,内容如下:

    var Boxing甲骨文 = artifacts.要求(“Boxing甲骨文”);

    模块.导出=函数(部署器){
        部署人员.部署(Boxing甲骨文);
    };

请看下面的例子: httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/migrations/2_deploy_contracts.js

甲骨文公司代码

对于这一步,只需从 httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/contracts/ 到你的 / / oracle-example / oracle /合同 文件夹:

  • Boxing甲骨文.索尔: 主oracle合同.
  • Ownable.索尔: For owner-only functions, as we used in the client contract already.
  • DateLib.索尔: 日期库. 我们将在本系列的第2部分更深入地研究它.

测试甲骨文

现在, 在项目的当前迭代中, 我们真的需要彻底测试我们的智能合约oracle, since that will be our base on which we’ll build the rest of the project. 那么,现在我们已经设置了oracle项目并复制了代码,我们想要:

  • 编译oracle.
  • 确保oracle运行.
  • 在松露控制台中运行一些函数以确保oracle按预期工作.

编译和迁移甲骨文

还在一个候机楼 / oracle-example / oracle /,执行如下命令. 再一次。, 这些步骤与我们已经完成的编译和迁移客户端契约的步骤相同.

松露编译

备用方案:使用我的recompile.Sh shell脚本(httpTo this://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/recompile.sh).

打开松露开发控制台:

松露开发

迁移到测试网络:

松露(develop)> migrate

运行并测试甲骨文

仍然在松露开发控制台, enter this to capture a usable pointer to the oracle contract:

松露(develop)> Boxing甲骨文.部署().then(inst => { instance = inst })

现在我们可以(也应该)在oracle契约上运行一套测试来测试它. Try running the following comm和s, each in turn, 和 examine the results.

松露(develop)> instance.测试Connection ()
...
松露(develop)> instance.getAllMatches ()
...
松露(develop)> instance.addTestData ()
...
松露(develop)> instance.getAllMatches ()
...

You are encouraged at this point to have a look through the oracle code, 查看有哪些公共方法可用, 阅读代码中的注释, 提出一些您自己要运行的测试(并在控制台中运行它们), 如上所示).

测试与调试

现在我们准备进行最后的测试:测试客户端合约是否可以调用已经在区块链上的oracle合约, 然后利用它的数据. 如果所有这些都有效,那么我们就有了一个客户端-oracle对,我们可以使用它进行进一步的实验. 运行端到端测试的步骤:

  • 编译并运行oracle合同
  • 编译并运行客户端契约
  • 获取oracle合同的地址
  • 在客户端合同中设置oracle地址
  • 将测试数据添加到oracle契约中
  • 测试我们是否可以在客户端合同中检索该数据

打开两个终端窗口:

  • 一个在 / oracle-example /客户/
  • 另一个在 / oracle-example / oracle /

我建议你保留 / oracle-example /客户/ 一个在左边开着,另一个 / oracle-example / oracle / one open on the right, 和 follow along closely to avoid confusion.

编译并运行甲骨文契约

中执行以下命令 / oracle-example / oracle / 终端:

bash重新编译.sh
松露开发 
松露(develop)> migrate 
松露(develop)> Boxing甲骨文.部署().then(inst => { instance = inst })

编译并运行客户端契约

中执行以下命令 / oracle-example /客户/ 终端:

bash重新编译.sh
松露开发 
松露(develop)> migrate 
松露(develop)> BoxingBets.部署().then(inst => { instance = inst })

获取甲骨文合同的地址

执行以下命令在 / oracle-example / oracle / 终端:

松露(develop)> instance.getAddress ()

复制 the address which is the 出put 从 this call; 和 use it in the next step.

在客户端合同中设置甲骨文地址

执行以下命令来松露 / oracle-example /客户/ 终端:

松露(develop)> instance.set甲骨文Address('')

然后测试一下:

松露(develop)> instance.测试甲骨文Connection ()

如果输出为 真正的,那么我们就可以开始了.

测试我们可以检索客户端合同中的数据

执行以下命令来松露 / oracle-example /客户/ 终端:

松露(develop)> instance.getBettableMatches ()

它应该返回一个空数组,因为还没有测试数据被添加到oracle端.

执行以下命令来松露 / oracle-example / oracle / 终端添加测试数据:

松露(develop)> instance.addTestData ()

执行以下命令来松露 / oracle-example /客户/ 终端,看是否可以从客户端拾取到新增的测试数据:

松露(develop)> instance.getBettableMatches ()

现在, if you take individual addresses 从 the array returned by getBettableMatches (),并将它们插入 getMatch ().

You are encouraged at this point to have a look through the client code, 查看有哪些公共方法可用, 阅读代码中的注释, 提出一些您自己要运行的测试(并在控制台中运行它们), 如上所述).

第一部分结语

我们的研究结果是有限的, 但我们的目标也是如此, 为了保持现实的节奏. 我们的客户还没有能力下注、处理资金、分配奖金等. What we do have—aside 从 the knowledge 和 experienced gained—is:

  • 一个主要功能的智能合约oracle
  • 可以连接到oracle并与之交互的客户端
  • 一个进一步发展和学习的框架

对于一篇短文来说,这还不算太糟.

In 本系列的第二部分, 我们将更深入地研究代码,并研究智能合约开发特有的一些功能,以及可靠性特有的一些语言功能. 在这一部分中被掩盖的许多事情将在下一部分中解释.

In 本系列的第三部分, we will discuss a bit ab出 the philosophy 和 design of smart contracts, 特别是与神谕的使用有关.

其他可选步骤

独自实验是学习的好方法. 如果您正在考虑如何扩展本教程以获得更多知识,这里有一些简单的建议(以下内容在第2部分和第3部分都不会涉及):

  • 将契约部署到Ganache(以前称为测试rpc),并运行相同的测试来验证功能.
  • 部署合约以启动或运行测试网,并运行相同的测试来验证功能.
  • Build a 网络3js front end for either the oracle, or the client (or both).

Good luck, 和 please feel free to contact me with any questions. I 可以’t guarantee a speedy reply necessarily, but I will do my best.

了解基本知识

  • 以太坊使用什么编程语言?

    The programming language used in Ethereum development is 可靠性. 它是一种受JavaScript、Python和c++启发的面向契约的编程语言.

  • 区块链中的oracle是什么?

    一个所谓的 区块链甲骨文 是一个可信的数据源,提供关于智能合约使用的各种状态和事件的信息.

  • 智能合约oracle是用来做什么的?

    智能合约预言机用于提供现实世界事件与数字合约之间的联系. oracle提供的外部数据可能(也可能不)触发智能合约的执行.

聘请Toptal这方面的专家.
现在雇佣
约翰R. 科辛斯基的头像
约翰R. 辛斯

位于 泰国清迈

成员自 2016年2月9日

作者简介

做了近二十年的全栈开发, 约翰研究过物联网, 区块链, 网络, 以及使用C/ c++的移动项目, .. NET, SQL和JS.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

专业知识

以前在

摩根士丹利(Morgan Stanley)

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.