个性化阅读
专注于IT技术分析

以太坊Oracle合同:设置和方向(1)

本文概述

以太坊智能合约不仅仅是”新的热点”。我相信他们(或相关事物)准备在即将到来的互联网新时代改变人们彼此开展业务的方式。时间会证明情况是否如此。

这是有关使用Solidity开发以太坊智能合约的三部分文章的第一篇, 其中最具体的内容是探讨使用所谓的” oracle”的合约-这些合约基本上是将数据泵入区块链以供其他智能合约使用的合约。

  • 第1部分:使用Truffle进行开发的介绍以及用于进一步实验的项目设置
  • 第2部分:深入研究代码以进行更深入的研究
  • 第3部分:具有智能合约的Oracle的概念性讨论

本系列文章的第1部分的目的不是要深入了解oracle合同的概念, 其背后的哲学, 甚至不是非常深入地了解它们是什么。我们的以太坊oracle教程这一部分的目标只是为了:

  • 让你通过Truffle建立智能合约。
  • 建立一个智能合约项目, 该项目将在第2部分和第3部分中为我们服务。
  • 介绍一些有关以太坊智能合约和智能合约编码的概念。
  • 介绍Truffle和智能合约的编译/运行/调试周期。

定义:Oracle。智能合约可从区块链外部访问数据的一种方式。甲骨文本身就是一种智能合约, 它从外界获取数据并将其放入区块链中供其他智能合约使用。

本文的第一部分将包括设置所有先决条件。然后, 我们将建立一个以太坊合约, 并用Truffle进行测试。最后, 我们将oracle与客户端分离, 并对其进行联合测试。

软件需求

  • 任何主要的操作系统都可以使用, 尽管某些安装和设置在不同的系统上当然会有所不同。我已经在Ubuntu Linux(16.04)上完成了所有这些工作。我在Windows上设置环境也没有问题。我没有尝试过Mac, 尽管我知道在Mac上也很常见。
  • 不必运行完整的eth节点;我们将使用Truffle, 它带有自己的测试网。如果你对自己的工作有所了解, 则可以使用你选择的任何其他测试网。就本教程而言, Truffle的本地开发测试网是最简单, 最易访问的。

知识要求

  • 关于区块链如何工作的基础知识
  • 了解什么是基于区块链的智能合约
  • 在智能合约开发方面有一些基本的, 打招呼的经验会有所帮助, 但如果你聪明又有野心, 则没有必要。 (我知道你是!)

本系列文章可以作为智能合约的第一个介绍, 但是很快就会发展成更高级的概念。如果这是你的第一个eth智能合约教程, 请准备好快速攀升。如果你有信心, 那就太好了;如果不是的话, 请先获得一个简单的” hello world”类型的教程, 或者两个。查阅以下以太坊文章或Cryptozombies中的一篇, 以了解入门知识。

注意事项:智能合约空间非常新, 因此变化很快。在撰写本文时, Solidity语法新功能可能已被弃用或已过时。格思版本可能来去去去。 Solidity总是在增加新的语言功能, 而不再使用旧的语言功能。当前正在开发许多新功能。因此, 如有必要, 请做好准备, 以使本文中的信息适应未来的新形势;如果你认真学习智能合约开发, 那么我对你充满信心。

示例应用程序的描述

用例:用户押注拳击比赛。

  • 用户可以拉出可投注拳击比赛的列表。
  • 用户可以选择一场比赛并在获胜者身上下注。
  • 用户可以投注高于指定下限的任何金额。
  • 如果用户的选择失败, 则用户将损失全部赌注。
  • 如果用户的选择权获胜, 则在房屋(合同所有者)获得一小部分奖金后, 用户将根据他/她的赌注大小和对比赛输家的总赌注获得一部分彩池。

什么是以太坊甲骨文?

智能合约仍然是新事物。他们还没有成为主流, 而且如何运作的许多方面尚未敲定和标准化。我将简要解释” oracle”思想背后的推动力, 并保持耐心;我们将在以后的部分中更深入地介绍它。

设计区块链合同与编写客户端服务器应用程序不同。一个重要的区别是, 与合同进行交互的数据必须已经在区块链上。没有退出区块链的呼声。语言不仅不支持它, 而且区块链范例也不支持它。合同可以采用基于以太坊的货币形式下注, 将其存储在合同中, 并在宣布比赛获胜者时根据公式将其释放到正确的钱包地址。但是合同如何知道获胜者呢?它无法查询REST API或类似的内容。它只能使用区块链中已经存在的数据!智能合约的许多许多用例都遇到了类似的问题, 除非它们可以与区块链之外的世界进行交互, 否则它们将受到严重限制。

如果合同只能与区块链上的数据进行交互, 那么一个显而易见的解决方案是将必要的数据注入到区块链中。这就是一个预言家。甲骨文是另一个合同, 它将数据注入到区块链中, 允许其他合同使用它。尽管这可能会引发关于信任和不信任的问题, 但是现在就接受这就是一个预言。在本系列的第3部分中, 我们将讨论这些细微差别。在我们的示例用例中, oracle将是将数据注入到区块链中的合同, 涉及以下方面:(a)确定了哪些匹配项, (b)谁赢得了这些匹配项。

搭建以太坊开发环境

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

  • Geth(目前可选)
  • 松露
  • Ganache CLI(可选)
  • 开发环境(可选)

本文没有空间作为环境设置的完整指南, 而只是作为粗略的指南。没关系, 因为针对你的特定操作系统已经有很多更完整的设置指南, 并且互联网确实不需要新的指南。因此, 我将带你快速上路, 并为你提供一些资源, 以便根据需要获取更多详细信息。准备按照系统要求和Google的指示安装要求和先决条件。

Oracle合同流程图

安装Geth(可选)

Geth安装屏幕截图

单击以查看完整图像。

Geth是以太坊的核心软件Go-ethereum;虽然根本不需要进行此练习, 但是任何以太坊开发人员都应该拥有并熟悉它。如果你要将智能合约部署到实时以太坊网络, 则很有必要。

  • http://www.talkcrypto.org/blog/2018/01/23/what-is-geth/
  • https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Ubuntu
  • https://github.com/ethereum/go-ethereum/wiki/Installation-instructions-for-Windows

安装松露

Truffle安装的屏幕截图

单击以查看完整图像。

松露是我们用于开发的主要内容, 绝对是本指南的要求。查找并遵循针对你的操作系统的特定说明来安装Truffle。以下是一些希望对你有所帮助的链接。

  • https://truffleframework.com/docs/truffle/getting-started/installation
  • https://github.com/trufflesuite/truffle
  • https://truffleframework.com/tutorials/how-to-install-truffle-and-testrpc-on-windows-for-blockchain-development

安装Ganache CLI(可选)

Ganache CLI安装屏幕快照

单击以查看完整图像。

我建议安装Ganache CLI用作另一个测试工具, 尽管我们在本教程中实际上不会使用它。它是可选的。

https://github.com/trufflesuite/ganache-cli

以太坊开发环境

使用任何简单的文本编辑器(例如Notepad ++, gedit, vi或你选择的任何文本编辑器或IDE)来完成整个教程将是非常有可能的。我个人正在使用带有以下扩展名的Visual Studio Code:

  • 坚固性
  • 坚固性扩展
  • 材质图标主题

注意:不需要扩展, 它们只是为了提供更好的编码环境。

设置代码

项目设置

Truffle是编译智能合约, 将其迁移到区块链的非常方便的工具, 并且还提供开发和调试实用程序。为了与Truffle集成, 必须进行一些项目设置。现在, 我们将在Truffle和目录结构中设置项目的外壳。只是坐下, 暂时按照机器人的步骤操作, 即可享受。

创建一个目录来存放所有代码;称之为oracle-example。

在根目录中, 创建两个子目录, 因为该项目最终将由两个子项目组成。创建目录:

  • / oracle-example / client
  • / oracle-example / oracle

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

运行命令truffle init。

请注意, 在创建的许多文件中有truffle-config.js和truffle.js。我们不需要两者, 因此请删除truffle-config.js(为避免混乱和混乱)。

我们需要编辑truffle.js, 以便将Truffle指向正确的测试方向。将truffle.js的内容替换为以下内容:

    module.exports = {
        networks: {
            development: {
                host: "localhost", port: 8545, network_id: "*" // Match any network id
            }
        }
    };

https://github.com/jrkosinski/oracle-example/tree/part1-step1/client/truffle.js

请注意, Truffle初始化创建了一个名为migrations的目录(oracle-example / client / migrations)。在该文件夹内应有一个名为1_initial_migration.js的文件。

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

    var BoxingBets = artifacts.require("BoxingBets");

    module.exports = function(deployer) {
        deployer.deploy(BoxingBets);
    };

https://github.com/jrkosinski/oracle-example/tree/part1-step1/client/migrations/2deploycontracts.js

添加代码

既然简单的设置已经完成, 我们就可以开始编码了。请记住, 本文的这一部分仍是介绍和设置, 因此我们将快速遍历代码。我们将在第2部分中对代码进行更深入的说明, 并在第3部分中对体系结构和概念进行更深入的讨论。也就是说, 我们将快速介绍代码中显而易见的一些核心概念。认真遵循以保持同步。

GitHub上提供了此过程中此步骤的完整代码:https://github.com/jrkosinski/oracle-example/tree/part1-step1

实体合同

Solidity中的”合同”大致类似于其他面向对象语言中的类。该语言本身已与Golang和JavaScript等进行了比较。 Solidity中的其他一些语言构造(我们将在后面提供示例)包括修饰符, 库和接口。合同支持继承(包括多重继承)。 Solidity合同文件的扩展名为.sol。

Oracle接口

将此文件添加到项目中:/oracle-example/client/contracts/OracleInterface.sol

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

oracle界面的屏幕截图

单击以查看完整图像。

通常, oracle接口就是这样一个接口。对于这第一次迭代, 它只是Solidity项目中包含的一个简单类, 仅作为一个占位符。在Truffle上成功编译并运行合同之后, 我们将在下一步中将其删除。稍后将其转换为实际接口后, 函数实现将为空。

客户合约

将此文件添加到你的项目:/oracle-example/client/contracts/BoxingBets.sol

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

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

编译并运行

现在是时候看看我们是否第一次一切都正确!

编译和迁移合同

在/ oracle-example / client /文件夹中打开一个终端

使用以下命令编译代码:

truffle compile
合同编制和迁移的屏幕截图

单击以查看完整图像。

合同编制和迁移的第二张屏幕截图

单击以查看完整图像。

备用:使用我的recompile.sh Shell脚本(https://github.com/jrkosinski/oracle-example/tree/part1-step1/client/recompile.sh)。

请注意, 你会看到很多警告, 因为我们的代码还没有最终形式!

打开Truffle开发控制台:

truffle develop

现在, 在Truffle开发者控制台中, 迁移到测试网络:

truffle(develop)> migrate

运行合同

在开发控制台提示符下, 输入以下代码行:

truffle(develop)> BoxingBets.deployed().then(inst => { instance = inst })

现在, ” instance”是引用BoxingBets合同的变量, 可用于调用其公共方法。

使用以下命令对其进行测试:

truffle(develop)> instance.test(3, 4) 

请注意, 我们在BoxingBets.sol中包含了一个公共的”测试”功能。它将你传递给它的任何两个数字加在一起, 只是为了证明合同正在执行代码, 并且我们可以从Truffle开发控制台中调用它。如果我们得到一个理智的回应(请参阅下文), 那么我们的工作就完成了(至少目前如此)。

分离以太坊Oracle

如果到目前为止一切顺利, 那么我们就过去了。接下来要做的是将oracle合同与BoxingBets合同分开。实际上, 甲骨文的合同将与区块链上的客户合同分开存在, 因此我们需要能够:

以太坊Oracle合约流程图
  • 通过区块链地址实例化。
  • 动态更改客户端合同用来引用oracle的oracle地址。

简而言之, 我们现在要做的是将oracle和客户端分离为两个单独的区块链合同实体, 并使它们彼此对话。客户端将通过地址实例化oracle并调用它。

客户合约

首先, 我们要更改客户合同(客户合同), 以便它指向oracle的动态接口, 而不是具体的类。然后, 我们将确保它从外部合同实例化oracle。

进入/oracle-example/client/contracts/OracleInterface.sol。如前所述, 目前这不是一个界面, 但我们即将使其成为一个界面。用以下内容替换其中的内容:

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

pragma solidity ^0.4.17;

contract OracleInterface {

    enum MatchOutcome {
        Pending, //match has not been fought to decision
        Underway, //match has started & is underway
        Draw, //anything other than a clear winner (e.g. cancelled)
        Decided     //index of participant who is the winner 
    }

    function getPendingMatches() public view returns (bytes32[]);

    function getAllMatches() public view returns (bytes32[]);

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

    function getMatch(bytes32 _matchId) public view returns (
        bytes32 id, string name, string participants, uint8 participantCount, uint date, MatchOutcome outcome, int8 winner);

    function getMostRecentMatch(bool _pending) public view returns (
        bytes32 id, string name, string participants, uint participantCount, uint date, MatchOutcome outcome, int8 winner);

    function testConnection() public pure returns (bool);

    function addTestData() public; 
}

在BoxingBets.sol中, 我们将替换以下行:

   OracleInterface internal boxingOracle = new OracleInterface(); 

这两行:

   address internal boxingOracleAddr = 0;
    OracleInterface internal boxingOracle = OracleInterface(boxingOracleAddr); 

现在, 我们想要的是一种动态设置Oracle地址的方法, 以及一个可以调用以查找当前Oracle地址的函数。将以下两个功能添加到BoxingBets.sol中:

   /// @notice sets the address of the boxing oracle contract to use 
    /// @dev setting a wrong address may result in false return value, or error 
    /// @param _oracleAddress the address of the boxing oracle 
    /// @return true if connection to the new oracle address was successful
    function setOracleAddress(address _oracleAddress) external onlyOwner returns (bool) {
        boxingOracleAddr = _oracleAddress;
        boxingOracle = OracleInterface(boxingOracleAddr); 
        return boxingOracle.testConnection();
    }

    /// @notice gets the address of the boxing oracle being used 
    /// @return the address of the currently set oracle 
    function getOracleAddress() external view returns (address) {
        return boxingOracleAddr;
    }

最后, 为了测试客户端与oracle之间的连接, 我们可以将BoxingBets中的test函数替换为测试oracle连接的函数:

   /// @notice for testing; tests that the boxing oracle is callable 
    /// @return true if connection successful 
    function testOracleConnection() public view returns (bool) {
        return boxingOracle.testConnection(); 
    }

可拥有的

请注意, setOracleAddress的定义后面有一个onlyOwner修饰符。即使该功能是公开的, 也限制了该功能不能由合同所有者以外的任何人调用。那不是语言功能。这是由Ownable合同提供给我们的, 该合同已从OpenZeppelin的通用公用事业Solidity合同库中删除。我们将在第2部分中对此进行详细介绍, 但是为了促进使用onlyOwner修饰符, 我们需要进行一些更改:

从https://github.com/jrkosinski/oracle-example/tree/part1-step2/client/contracts/Ownable.sol复制Ownable.sol到/ oracle-example / client / contracts /中。

在BoxingBets.sol的顶部添加对它的引用, 如下所示:

import "./Ownable.sol";

(你可以将其添加到导入OracleInterface.sol的行的下面。)

修改BoxingBets的合同声明, 使其从Ownable继承自此:

contract BoxingBets {

对此:

contract BoxingBets is Ownable {

我们应该一切就绪。完整的代码在这里, 以防万一你迷路了:https://github.com/jrkosinski/oracle-example/tree/part1-step2/client/contracts

甲骨文合同

设定

现在, BoxingBets合同正尝试按地址引用一个完全独立的合同(即oracle), 我们的下一个工作是创建该oracle合同。因此, 现在我们将创建一个单独的项目, 其中将包含oracle合同。基本上与我们为客户合同项目所做的设置相同;也就是说, 设置Truffle进行编译和开发。

你应该已经有一个在上一步中创建的名为/ oracle-example / oracle /的文件夹(否则, 请继续并立即创建该空目录)。在该目录中打开一个终端。

  • 运行命令truffle init。
  • 删除/oracle-example/oracle/truffle-config.js。
  • 像这样编辑/oracle-example/oracle/truffle.js:
    module.exports = {
        networks: {
            development: {
                host: "localhost", port: 8545, network_id: "*" // Match any network id
            }
        }
    };

在此处查看示例:https://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/truffle.js

在/ oracle-example / oracle / migrations /内, 创建一个名为2_deploy_contracts.js的文件, 其内容如下:

    var BoxingOracle = artifacts.require("BoxingOracle");

    module.exports = function(deployer) {
        deployer.deploy(BoxingOracle);
    };

在此处查看示例:https://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/migrations/2_deploy_contracts.js

Oracle代码

对于此步骤, 只需将https://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/contracts/中的以下三个文件复制到/ oracle-example / oracle / contracts /文件夹中:

  • BoxingOracle.sol:主Oracle合同。
  • Ownable.sol:对于仅所有者功能, 如我们在客户端合同中所使用的。
  • DateLib.sol:日期库。我们将在本系列的第2部分中对其进行更深入的研究。

测试Oracle

现在, 在项目的当前迭代中, 我们确实需要彻底测试我们的智能合约Oracle, 因为这将是我们构建项目其余部分的基础。因此, 既然我们已经设置了oracle项目并复制了代码, 我们将想要:

  • 编译oracle。
  • 确保oracle运行。
  • 在Truffle控制台中运行一些功能, 以确保oracle正常工作。

编译和迁移Oracle

仍在打开/ oracle-example / oracle /的终端中, 运行以下命令。同样, 这些步骤与我们已经完成的编译和迁移客户合同的步骤相同。

truffle compile

备用:使用我的recompile.sh Shell脚本(https://github.com/jrkosinski/oracle-example/tree/part1-step2/oracle/recompile.sh)。

打开Truffle开发控制台:

truffle develop

迁移到测试网络:

truffle(develop)> migrate

运行和测试Oracle

仍在Truffle开发控制台中, 输入以下内容以捕获指向oracle合同的可用指针:

truffle(develop)> BoxingOracle.deployed().then(inst => { instance = inst })

现在, 我们可以(并且应该)对我们的oracle合同运行一套测试以对其进行测试。尝试依次运行以下命令, 并检查结果。

truffle(develop)> instance.testConnection()
...
truffle(develop)> instance.getAllMatches()
...
truffle(develop)> instance.addTestData()
...
truffle(develop)> instance.getAllMatches()
...

鼓励你在这一点上浏览一下oracle代码, 查看可用的公共方法, 阅读代码中的注释, 并提出一些自己的测试来运行(并在控制台中运行它们, 如如上所示)。

测试与调试

现在, 我们可以进行最终测试了:测试客户合同可以调用区块链上已经存在的oracle合同, 并获取和使用其数据。如果所有这些都有效, 那么我们就有了一个Client-Oracle对, 可以用于进一步的实验。我们运行端到端测试的步骤:

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

打开两个终端窗口:

  • / oracle-example / client /中有一个
  • 另一个在/ oracle-example / oracle /

我建议你将/ oracle-example / client /左侧打开, 将/ oracle-example / oracle /右侧打开, 并密切注意以避免混淆。

编译并运行Oracle Contract

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

bash recompile.sh
truffle develop 
truffle(develop)> migrate 
truffle(develop)> BoxingOracle.deployed().then(inst => { instance = inst })

编译并运行客户合同

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

bash recompile.sh
truffle develop 
truffle(develop)> migrate 
truffle(develop)> BoxingBets.deployed().then(inst => { instance = inst })

获取Oracle合同的地址

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

truffle(develop)> instance.getAddress()

复制此调用输出的地址;并在下一步中使用它。

在客户合同中设置Oracle地址

执行以下命令在/ oracle-example / client /终端中松散:

truffle(develop)> instance.setOracleAddress('<insert address here, single quotes included>')

并测试一下:

truffle(develop)> instance.testOracleConnection()

如果输出为true, 那么我们很好。

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

执行以下命令在/ oracle-example / client /终端中松散:

truffle(develop)> instance.getBettableMatches()

它应该返回一个空数组, 因为尚未向oracle端添加任何测试数据。

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

truffle(develop)> instance.addTestData()

执行以下命令在/ oracle-example / client /终端中松散, 以查看我们是否可以从客户端获取新添加的测试数据:

truffle(develop)> instance.getBettableMatches()

现在, 如果你从getBettableMatches()返回的数组中获取单个地址, 然后将其插入getMatch()。

在这一点上, 我们鼓励你浏览客户端代码, 查看可用的公共方法, 阅读代码中的注释, 并提出一些自己的测试来运行(并在控制台中运行它们, 如以上)。

第一部分结论

我们从这次练习中获得的结果是有限的, 但是为了保持现实的步伐, 我们的目标也是如此。我们的客户还没有下注, 处理资金, 分配奖金等的能力。除了所拥有的知识和经验之外, 我们所拥有的是:

  • 功能最强大的智能合约Oracle
  • 可以连接到Oracle并与之交互的客户端
  • 进一步发展和学习的框架

对于短篇文章来说还不错。

在本系列的第二部分中, 我们将更深入地研究代码, 并研究智能合约开发所特有的某些功能以及Solidity特有的某些语言功能。在下一部分中将解释许多本章仅涉及的内容。

在本系列的第三部分中, 我们将讨论一些有关智能合约的哲学和设计, 特别是关于将它们与oracle结合使用的信息。

进一步的可选步骤

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

  • 将合同部署到Ganache(以前称为testrpc)并运行相同的测试以验证功能。
  • 将合同部署到ropsten或rinkeby测试网, 并运行相同的测试以验证功能。
  • 为oracle或客户端(或两者)构建一个web3js前端。

祝你好运, 如有任何疑问, 请随时与我联系。我不能保证一定能尽快答复, 但我会尽力而为。

赞(0)
未经允许不得转载:srcmini » 以太坊Oracle合同:设置和方向(1)

评论 抢沙发

评论前必须登录!