本文概述
- 什么是以太坊甲骨文?
- 搭建以太坊开发环境
- 设置代码
- 分离以太坊Oracle
- 甲骨文合同
- 测试与调试
- 第一部分结论
这是有关使用Solidity开发以太坊智能合约的三部分文章的第一篇, 其中最具体的内容是探讨使用所谓的” oracle” 的合约-这些合约基本上是将数据泵入区块链以供其他智能合约使用的合约。
- 第1部分:使用Truffle进行开发的介绍以及用于进一步实验的项目设置
- 第2部分:深入研究代码以进行更深入的研究
- 第3部分:具有智能合约的Oracle的概念性讨论
- 让你通过Truffle建立智能合约。
- 建立一个智能合约项目, 该项目将在第2部分和第3部分中为我们服务。
- 介绍一些有关以太坊智能合约和智能合约编码的概念。
- 介绍Truffle和智能合约的编译/运行/调试周期。
定义:Oracle。智能合约可从区块链外部访问数据的一种方式。甲骨文本身就是一种智能合约, 它从外界获取数据并将其放入区块链中供其他智能合约使用。本文的第一部分将包括设置所有先决条件。然后, 我们将建立一个以太坊合约, 并用Truffle进行测试。最后, 我们将oracle与客户端分离, 并对其进行联合测试。
软件需求
- 任何主要的操作系统都可以使用, 尽管某些安装和设置在不同的系统上当然会有所不同。我已经在Ubuntu Linux(16.04)上完成了所有这些工作。我在Windows上设置环境也没有问题。我没有尝试过Mac, 尽管我知道在Mac上也很常见。
- 不必运行完整的eth节点;我们将使用Truffle, 它带有自己的测试网。如果你对自己的工作有所了解, 则可以使用你选择的任何其他测试网。就本教程而言, Truffle的本地开发测试网是最简单, 最易访问的。
- 关于区块链如何工作的基础知识
- 了解什么是基于区块链的智能合约
- 在智能合约开发方面有一些基本的, 打招呼的经验会有所帮助, 但如果你聪明又有野心, 则没有必要。 (我知道你是!)
注意事项:智能合约空间非常新, 因此变化很快。在撰写本文时, Solidity语法新功能可能已被弃用或已过时。格思版本可能来去去去。 Solidity总是在增加新的语言功能, 而不再使用旧的语言功能。当前正在开发许多新功能。因此, 如有必要, 请做好准备, 以使本文中的信息适应未来的新形势;如果你认真学习智能合约开发, 那么我对你充满信心。示例应用程序的描述
用例:用户押注拳击比赛。
- 用户可以拉出可投注拳击比赛的列表。
- 用户可以选择一场比赛并在获胜者身上下注。
- 用户可以投注高于指定下限的任何金额。
- 如果用户的选择失败, 则用户将损失全部赌注。
- 如果用户的选择权获胜, 则在房屋(合同所有者)获得一小部分奖金后, 用户将根据他/她的赌注大小和对比赛输家的总赌注获得一部分彩池。
设计区块链合同与编写客户端服务器应用程序不同。一个重要的区别是, 与合同进行交互的数据必须已经在区块链上。没有退出区块链的呼声。语言不仅不支持它, 而且区块链范例也不支持它。合同可以采用基于以太坊的货币形式下注, 将其存储在合同中, 并在宣布比赛获胜者时根据公式将其释放到正确的钱包地址。但是合同如何知道获胜者呢?它无法查询REST API或类似的内容。它只能使用区块链中已经存在的数据!智能合约的许多许多用例都遇到了类似的问题, 除非它们可以与区块链之外的世界进行交互, 否则它们将受到严重限制。
如果合同只能与区块链上的数据进行交互, 那么一个显而易见的解决方案是将必要的数据注入到区块链中。这就是一个预言家。甲骨文是另一个合同, 它将数据注入到区块链中, 允许其他合同使用它。尽管这可能会引发关于信任和不信任的问题, 但是现在就接受这就是一个预言。在本系列的第3部分中, 我们将讨论这些细微差别。在我们的示例用例中, oracle将是将数据注入到区块链中的合同, 涉及以下方面:(a)确定了哪些匹配项, (b)谁赢得了这些匹配项。
搭建以太坊开发环境 对于基本设置, 我们将安装:
- Geth(目前可选)
- 松露
- Ganache CLI(可选)
- 开发环境(可选)
文章图片
安装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。以下是一些希望对你有所帮助的链接。
- 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用作另一个测试工具, 尽管我们在本教程中实际上不会使用它。它是可选的。
https://github.com/trufflesuite/ganache-cli以太坊开发环境
使用任何简单的文本编辑器(例如Notepad ++, gedit, vi或你选择的任何文本编辑器或IDE)来完成整个教程将是非常有可能的。我个人正在使用带有以下扩展名的Visual Studio Code:
- 坚固性
- 坚固性扩展
- 材质图标主题
注意:不需要扩展, 它们只是为了提供更好的编码环境。设置代码 项目设置
Truffle是编译智能合约, 将其迁移到区块链的非常方便的工具, 并且还提供开发和调试实用程序。为了与Truffle集成, 必须进行一些项目设置。现在, 我们将在Truffle和目录结构中设置项目的外壳。只是坐下, 暂时按照机器人的步骤操作, 即可享受。
创建一个目录来存放所有代码;称之为oracle-example。
在根目录中, 创建两个子目录, 因为该项目最终将由两个子项目组成。创建目录:
- / oracle-example / client
- / oracle-example / oracle
运行命令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接口就是这样一个接口。对于这第一次迭代, 它只是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-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运行。
- 在Truffle控制台中运行一些功能, 以确保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 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前端。
推荐阅读
- 情感分析准确性的四大误区
- 在使用“Contains”时深入研究实体框架的性能
- 使用Firebase在Angular中进行状态管理
- 后端(使用Gatsby.js和Node.js进行静态网站更新)
- 数据仓库开发的三项原则
- 算术(使用Orchestrators扩展微服务应用程序)
- 如何使用Firebase身份验证构建基于角色的API
- Windows 10上免费的28款最佳OCR软件下载推荐合集(哪个最好())
- Windows 10如何修复无法安装累积更新KB5008212(解决办法)