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

以太坊Oracle合同:统一代码功能(2)

点击下载

本文概述

在这三部分的第一部分中, 我们经历了一个小教程, 该教程为我们提供了一个简单的Oracle合同。描述了设置(使用松露), 编译代码, 部署到测试网络, 运行和调试的机制和过程;但是, 代码的许多细节都是以手工方式掩盖的。因此, 现在, 正如我们所承诺的, 我们将研究一些语言功能, 这些功能对于Solidity智能合约开发是唯一的, 并且对于此特定的合同Oracle场景是唯一的。尽管我们不会费力地查看每个细节(如果你愿意, 我会在你的进一步研究中留给你), 但我们将尝试利用代码中最引人注目的, 最有趣和最重要的功能。

为了简化此操作, 我建议你打开自己的项目版本(如果有的话), 或者准备好代码以供参考。

此时的完整代码可以在这里找到:https://github.com/jrkosinski/oracle-example/tree/part2-step1

以太坊和团结

Solidity并不是唯一可用的智能合约开发语言, 但我认为可以肯定地说, 它是以太坊智能合约中最普遍和最受欢迎的语言。当然, 在撰写本文时, 它是最受欢迎的支持和信息。

以太坊关键功能示意图

团结是面向对象的, 是图灵完备的。也就是说, 你很快就会意识到它的内置(和完全有意的)局限性, 这使智能合约编程与普通的”让我们做这件事”黑客大为不同。

实体版

这是每本Solidity代码诗的第一行:

pragma solidity ^0.4.17;

你看到的版本号将有所不同, 因为Solidity尚处于年轻阶段, 并且正在快速变化和发展。版本0.4.17是我在示例中使用的版本;本出版物发行时的最新版本为0.4.25。

目前你正在阅读的最新版本可能完全不同。 Solidity正在(或至少计划中)有许多不错的功能, 我们将在此进行讨论。

这是不同Solidity版本的概述。

专家提示:你还可以指定一个版本范围(尽管我不太经常这样做), 如下所示:

pragma solidity >=0.4.16 <0.6.0;

Solidity编程语言功能

Solidity具有许多现代程序员熟悉的语言功能, 以及一些独特的(至少对我而言)与众不同的语言功能。据说它受C ++, Python和JavaScript的启发-所有这些我个人都很熟悉, 但是Solidity似乎与所有这些语言都截然不同。

合同

.sol文件是代码的基本单位。在BoxingOracle.sol中, 注意第9行:

contract BoxingOracle is Ownable {

由于类是面向对象语言中逻辑的基本单元, 因此契约是Solidity中逻辑的基本单元。现在只需说合同是Solidity的”类”就足够了(对于面向对象的程序员来说, 这是一个简单的飞跃)。

遗产

实体合同完全支持继承, 并且可以按你期望的那样工作;私有合同成员不是继承人, 受保护的合同成员和公共合同成员是继承人。如你所愿, 支持重载和多态。

contract BoxingOracle is Ownable {

在上面的语句中, ” is”关键字表示继承。 BoxingOracle继承自Ownable。 Solidity还支持多重继承。多个继承由逗号分隔的类名列表指示, 如下所示:

contract Child is ParentA, ParentB, ParentC {
…

(在我看来)在构造继承模型时不要太复杂, 这是个好主意, 但这篇有趣的文章是关于所谓的钻石问题的Solidity。

枚举

Solidity支持枚举:

    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 
    }

正如你所期望的(与熟悉的语言相同), 每个枚举值都分配有一个以0开头的整数值。如Solidity文档中所述, 枚举值可以转换为所有整数类型(例如uint, uint16, uint32, 等), 但不允许进行隐式转换。这意味着必须将它们显式转换(例如, 转换为uint)。

Solidity Docs:枚举枚举教程

结构

结构是枚举的另一种创建用户定义的数据类型的方法。所有C / C ++基础编码人员和像我这样的老家伙都熟悉结构。来自BoxingOracle.sol的第17行的结构示例:

//defines a match along with its outcome
    struct Match {
        bytes32 id;
        string name;
        string participants;
        uint8 participantCount;
        uint date; 
        MatchOutcome outcome;
        int8 winner;
    }

给所有旧的C程序员请注意:Solidity中的结构”打包”是一回事, 但是有一些规则和警告。不必假设它的工作原理与C语言相同;检查文档并了解你的情况, 以确定在特定情况下包装是否对你有帮助。

固体结构填料

创建后, 结构就可以在代码中作为本地数据类型进行寻址。这是上面创建的struct类型的”实例化”语法的示例:

Match match = Match(id, "A vs. B", "A|B", 2, block.timestamp, MatchOutcome.Pending, 1); 

实体数据类型

这使我们进入了Solidity中数据类型的最基本主题。实体支持哪些数据类型?实体是静态类型的, 在撰写本文时, 必须明确声明数据类型并将其绑定到变量。

以太坊实体中的数据类型

实体数据类型

布尔值

支持布尔类型, 名称为bool, 值true或false

数值类型

支持从int8 / uint8到int256 / uint256(分别是8位整数到256位整数)的有符号和无符号整数类型。 uint类型是uint256的简写(同样, int是int256的简写)。

值得注意的是, 不支持浮点类型。为什么不?好吧, 一方面, 众所周知, 在处理货币价值时, 浮点变量是个坏主意, 因为总的来说, 价值可能会损失lost尽。醚值以wei表示, 它是以太的1 / 1, 000, 000, 000, 000, 000, 000, 并且必须满足所有目的的精度;你不能将醚分解成更小的部分。

目前部分支持定点值。根据Solidity文档:” Solidity尚未完全支持定点数。它们可以声明, 但不能分配给它们或从它们分配。”

https://hackernoon.com/a-note-on-numbers-in-ethereum-and-javascript-3e6ac3b2fad9

注意:在大多数情况下, 最好只使用uint, 因为减小变量的大小(例如, 减小到uint32)实际上会增加汽油费用, 而不是像你期望的那样减少费用。作为一般经验法则, 请使用uint, 除非你确定有充分的理由这样做。

字符串类型

Solidity中的字符串数据类型是一个有趣的话题。你可能会根据与谁交谈而获得不同的意见。在Solidity中有一个字符串数据类型, 这是事实。我的看法(可能是大多数人都同意的)是它没有提供太多功能。字符串解析, 连接, 替换, 修剪, 甚至计数字符串的长度:都不存在你可能希望从字符串类型中得到的所有内容, 因此, 这是你的责任(如果需要)。有些人用bytes32代替字符串;也可以做到。

关于Solidity字符串的有趣文章

我的看法:编写自己的字符串类型并将其发布以供一般使用可能是一个有趣的练习。

地址类型

也许对于Solidity来说是唯一的, 我们有一个地址数据类型, 专门用于以太坊钱包或合约地址。这是一个20字节的值, 专门用于存储该特定大小的地址。另外, 它具有专门用于此类地址的类型成员。

address internal boxingOracleAddr = 0x145ca3e014aaf5dca488057592ee45305d9b3a22; 

地址数据类型

日期时间类型

本质上, Solidity中没有本地的Date或DateTime类型, 例如JavaScript。 (哦, 不, Solidity在每个段落中听起来都越来越糟!?)日期本身就是uint(uint256)类型的时间戳。由于块时间戳是Unix风格的时间戳, 因此通常将它们作为Unix风格的时间戳(以秒为单位, 而不是毫秒)来处理。如果出于各种原因而需要使用人类可读的日期, 则可以使用开源库。你可能会注意到, 我在BoxingOracle中使用了一个:DateLib.sol。 OpenZeppelin还具有date实用程序以及许多其他类型的常规实用程序库(我们将很快使用Solidity的库功能)。

专家提示:无论是知识还是预先编写的通用代码, OpenZeppelin都是不错的来源(但当然不是唯一的不错的来源), 可以帮助你建立合同。

对应

请注意, BoxingOracle.sol的第11行定义了一种称为映射的内容:

mapping(bytes32 => uint) matchIdToIndex;

Solidity中的映射是一种用于快速查找的特殊数据类型。本质上是一个查找表或类似于哈希表的表, 其中包含的数据驻留在区块链本身上(当映射被定义为类成员时)。在合同执行期间, 我们可以将数据添加到映射中, 类似于将数据添加到哈希表中, 然后再查找已添加的值。再次注意, 在这种情况下, 我们添加的数据将添加到区块链本身, 因此它将持久存在。如果我们今天将其添加到纽约的地图中, 那么从现在起的一周内, 伊斯坦布尔的某人可以阅读它。

从BoxingOracle.sol的第71行添加到映射的示例:

matchIdToIndex[id] = newIndex+1

从BoxingOracle.sol的第51行读取映射的示例:

uint index = matchIdToIndex[_matchId]; 

也可以从映射中删除项目。该项目未使用它, 但是看起来像这样:

delete matchIdToIndex[_matchId];

返回值

你可能已经注意到, Solidity可能与Javascript有点表面上的相似之处, 但它并没有继承JavaScript的类型和定义的松散性。必须以相当严格和受限制的方式定义合同代码(考虑到用例, 这可能是一件好事)。考虑到这一点, 请考虑BoxingOracle.sol第40行中的函数定义

function _getMatchIndex(bytes32 _matchId) private view returns (uint) { ... }

好的, 让我们首先快速浏览一下其中包含的内容。函数将其标记为功能。 _getMatchIndex是函数名称(下划线是表示私有成员的约定, 我们将在后面讨论)。它使用一个字节类型为32的参数, 该参数名为_matchId(这是使用下划线约定表示函数参数)。关键字private实际上使成员在范围内变为私有, view告诉编译器该函数不会修改区块链上的任何数据, 最后:~~~ solidity返回(uint)~~~

这表示该函数返回一个uint(返回void的函数在这里根本没有return子句)。为什么在括号中加上uint?这是因为Solidity函数可以并且经常返回元组。

现在考虑第166行的以下定义:

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

看看这个的return子句!它返回一, 二…七种不同的东西。 OK, 因此, 此函数将这些内容作为元组返回。为什么?在开发过程中, 你经常会发现自己需要返回一个结构(如果是JavaScript, 则可能想返回一个JSON对象)。好吧, 在撰写本文时(尽管将来可能会更改), Solidity不支持从公共函数返回结构。因此, 你必须返回元组。如果你是Python专家, 可能已经对tuple感到满意了。不过, 许多语言并没有真正支持它们, 至少不是这样。

有关将元组作为返回值返回的示例, 请参见第159行:

return (_matchId, "", "", 0, 0, MatchOutcome.Pending, -1);

以及我们如何接受这样的返回值?我们可以这样:

var (id, name, part, count, date, outcome, winner) = getMostRecentMatch(false); 

另外, 你可以预先使用正确的类型显式声明变量:

//declare the variables 
bytes32 id; 
string name; 
... etc... 
int8 winner; 

//assign their values 
(id, name, part, count, date, outcome, winner) = getMostRecentMatch(false); 

现在, 我们声明了7个变量来保存7个返回值, 我们现在可以使用它们。否则, 假设我们只需要一个或两个值, 我们可以说:

//declare the variables 
bytes32 id; 
uint date;

//assign their values 
(id, , , , date, , ) = getMostRecentMatch(false); 

看看我们在那里做了什么?我们只有我们感兴趣的两个。请查看所有这些逗号。我们必须仔细计数!

进口货

BoxingOracle.sol的第3行和第4行是导入的:

import "./Ownable.sol";
import "./DateLib.sol";

如你所料, 这些都是从与BoxingOracle.sol相同的合同项目文件夹中存在的代码文件中导入定义的。

修饰符

注意, 函数定义附加了许多修饰符。首先, 有可见性:私有, 公共, 内部和外部-功能可见性。

此外, 你会看到纯关键字和视图。这些向编译器指示函数将进行什么样的更改(如果有)。这很重要, 因为这种事情是运行该功能的最终气体成本的一个因素。有关说明, 请参见此处:Solidity Docs。

最后, 我真正想讨论的是自定义修饰符。看看BoxingOracle.sol的第61行:

function addMatch(string _name, string _participants, uint8 _participantCount, uint _date) onlyOwner public returns (bytes32) {

注意在” public”关键字之前的onlyOwner修饰符。这表明只有合同的所有者才能调用此方法!尽管非常重要, 但这不是Solidity的固有功能(尽管将来可能会)。实际上, onlyOwner是我们自己创建和使用的自定义修饰符的示例。我们来看一下。

首先, 修饰符在文件Ownable.sol中定义, 你可以看到我们已经在BoxingOracle.sol的第3行中导入了该文件:

import "./Ownable.sol"

请注意, 为了利用修饰符, 我们使BoxingOracle从Ownable继承。在Ownable.sol内部, 在第25行, 我们可以在” Ownable”合约内部找到修饰符的定义:

modifier onlyOwner() {
	require(msg.sender == owner);
	_;
}

(顺便说一句, 该拥有的合同摘自OpenZeppelin的公共合同之一。)

请注意, 此东西被声明为修饰符, 表明我们可以按需使用它来修改函数。请注意, 修饰符的内容是” require”语句。 Require语句类似于断言, 但不适用于调试。如果require语句的条件失败, 则该函数将引发异常。因此, 请解释此” require”语句:

require(msg.sender == owner);

我们可以说这意味着:

if (msg.send != owner) 
	throw an exception; 

而且, 实际上, 在Solidity 0.4.22及更高版本中, 我们可以向该require语句添加错误消息:

require(msg.sender == owner, "Error: this function is callable by the owner of the contract, only"); 

最后, 在看起来很奇怪的行中:

_; 

下划线是”在这里, 执行修改后的功能的全部内容”的简写。因此, 实际上, 将首先执行require语句, 然后执行实际功能。因此, 就像将这行逻辑添加到修改后的函数之前一样。

当然, 修饰符可以做更多的事情。检查文档:文档。

实体库

Solidity的语言功能称为库。我们在DateLib.sol项目中有一个示例。

Solidity库实施!

这是一个用于更轻松地处理日期类型的库。在第4行将其导入BoxingOracle:

import "./DateLib.sol";

它在第13行使用:

using DateLib for DateLib.DateTime;

DateLib.DateTime是从DateLib合约(作为成员公开;请参见DateLib.sol的第4行)中扩展而来的一种结构, 在此我们声明我们在”使用” DateLib库用于某种数据类型。因此, 该库中声明的方法和操作将适用于我们所说的数据类型。这就是在Solidity中使用库的方式。

例如, 请查看OpenZeppelin的某些库中的数字, 例如SafeMath。它们可以应用于本机(数字)Solidity数据类型(而此处我们已将库应用于自定义数据类型), 并且得到了广泛使用。

介面

与主流的面向对象语言一样, 也支持接口。 Solidity中的接口被定义为协定, 但功能的功能主体被省略。有关接口定义的示例, 请参见OracleInterface.sol。在此示例中, 该接口用作oracle合同的替代, 其内容位于具有单独地址的单独合同中。

命名约定

当然, 命名约定不是全局规则。作为程序员, 我们知道我们可以自由地遵循吸引我们的编码和命名约定。另一方面, 我们确实希望其他人可以舒适地阅读和使用我们的代码, 因此需要一定程度的标准化。

项目概况

现在, 我们已经研究了有问题的代码文件中存在的一些常规语言功能, 我们可以开始对该项目的代码本身进行更具体的研究。

因此, 让我们再次澄清该项目的目的。该项目的目的是提供使用Oracle的智能合约的半现实(或伪现实)演示和示例。从本质上讲, 这只是一个调用另一个单独合同的合同。

该示例的业务案例可以说明如下:

  • 用户想在拳击比赛中进行各种大小的下注, 为下注付钱(以太), 并在获胜时以及在获胜时收集其奖金。
  • 用户通过智能合约进行这些投注。 (在实际的用例中, 这将是具有web3前端的完整DApp;但是我们仅在合同方面进行检查。)
  • 第三方维护单独的智能合约(oracle)。它的工作是维护一个带有当前状态(待决, 进行中, 已完成等)以及(如果完成)获胜者的拳击比赛的列表。
  • 主合同从oracle中获取挂起的匹配列表, 并将这些列表作为”可匹配”呈现给用户。
  • 主合同接受投注直到比赛开始。
  • 决定一场比赛之后, 主合同根据简单的算法将获利和亏损进行分配, 自己进行减价, 并根据要求支付赢利(失败者仅输掉全部股份)。

投注规则:

  • 有一个已定义的最低下注(在wei中定义)。
  • 没有最大赌注;用户可以打赌他们喜欢的任何高于最低金额的金额。
  • 用户可以下注直到比赛开始。

奖金分配算法:

  • 所有收到的赌注都放在一个”底池”中。
  • 从锅中取出一小部分作为房屋。
  • 每个获胜者都将获得一定比例的底池, 直接与其下注的相对大小成正比。
  • 比赛确定后, 只要第一个用户请求结果, 就计算赢利。
  • 奖金是根据用户要求授予的。
  • 万一出现平局, 没人会赢-所有人都拿回自己的股份, 房子也不会减少。

BoxingOracle:Oracle合同

提供的主要功能

甲骨文有两个接口, 你可以说:一个提供给合同的”所有者”和维护者, 另一个提供给公众。即, 消耗预言的合同。维护者, 它提供了将数据馈入合同的功能, 实际上是从外界获取数据并将其放到区块链上。对于公众来说, 它提供了对所述数据的只读访问。请务必注意, 合同本身限制了非所有者无法编辑任何数据, 但是公开授予对该数据的只读访问权限却不受限制。

给用户:

  • 列出所有比赛
  • 列出待处理的比赛
  • 获取特定比赛的详细信息
  • 获取特定比赛的状态和结果

致拥有者:

  • 输入比赛
  • 更改比赛状态
  • 设定比赛结果
用户和所有者访问元素的插图

用户的故事:

  • 一场新的拳击比赛宣布, 并于5月9日确认。
  • 我(合同的维护者)(也许我是著名的体育网络或新的零售店)将即将到来的比赛添加到区块链上oracle的数据中, 状态为”待处理”。现在任何人或任何合同都可以查询并使用他们喜欢的数据。
  • 比赛开始时, 我将比赛的状态设置为”进行中”。
  • 比赛结束后, 我将比赛状态设置为”完成”, 并修改比赛数据以表示获胜者。

Oracle代码审查

这篇评论完全基于BoxingOracle.sol。行号引用该文件。

在第10行和第11行, 我们声明要存储匹配项的位置:

	Match[] matches; 
	mapping(bytes32 => uint) matchIdToIndex; 

matchs是一个用于存储match实例的简单数组, 而该映射只是将唯一的match ID(一个bytes32值)映射到其在数组中的索引的便利, 这样, 如果有人将匹配的原始ID交给我们, 我们可以使用此映射找到它。

在第17行, 我们定义并说明了匹配结构:

    //defines a match along with its outcome
    struct Match {
        bytes32 id;             //unique id
        string name;            //human-friendly name (e.g., Jones vs. Holloway)
        string participants;    //a delimited string of participant names
        uint8 participantCount; //number of participants (always 2 for boxing matches!) 
        uint date;              //GMT timestamp of date of contest
        MatchOutcome outcome;   //the outcome (if decided)
        int8 winner;            //index of the participant who is the winner
    }

    //possible match outcomes 
    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 
    }

第61行:addMatch函数仅供合同所有者使用;它允许为存储的数据添加新的匹配项。

第80行:函数declareOutcome允许合同所有者将匹配设置为”已决定”, 从而设置获胜者。

102-166行:公众均可调用以下功能。这是通常向公众开放的只读数据:

  • 函数getPendingMatches返回当前状态为” pending”的所有匹配的ID列表。
  • 函数getAllMatches返回所有匹配的ID的列表。
  • 函数getMatch返回由ID指定的单个匹配项的完整详细信息。

第193-204行声明了主要用于测试, 调试和诊断的功能。

  • 函数testConnection只是测试我们是否可以调用合同。
  • 函数getAddress返回此合同的地址。
  • 函数addTestData将一堆测试匹配项添加到匹配项列表中。

在继续下一步之前, 可以随意浏览一下代码。我建议再次以调试模式运行oracle合同(如本系列的第1部分中所述), 调用不同的函数并检查结果。

BoxingBets:客户合同

定义客户合同(投注合同)负责什么以及不负责什么很重要。客户合同不负责维护真实拳击比赛清单或声明其结果。我们”信任”该服务的预言家(是的, 我知道, 有一个敏感的词-哦, 我们将在第3部分中对此进行讨论)。客户合同负责接受投注。它负责根据获胜结果(从甲骨文收到的结果)将获胜奖金进行划分并转移到获胜者帐户的算法。

此外, 所有内容都是基于拉动的, 没有事件或推送。合同从oracle中提取数据。合同从预告片中提取比赛结果(响应用户请求), 合同计算彩金并响应用户请求转移奖金。

提供的主要功能

  • 列出所有未决的比赛
  • 获取特定比赛的详细信息
  • 获取特定比赛的状态和结果
  • 下注
  • 请求/接收奖金

客户代码审查

该评论完全基于BoxingBets.sol;行号引用该文件。

第12和13行(合同中的第一行代码)定义了一些映射, 我们将在其中存储合同的数据。

第12行将用户地址映射到ID列表。这会将用户映射到属于该用户的投注ID列表。因此, 对于任何给定的用户地址, 我们可以快速获得该用户进行的所有投注的列表。

    mapping(address => bytes32[]) private userToBets;

第13行将比赛的唯一ID映射到下注实例列表。这样, 对于任何给定的比赛, 我们都可以获得该比赛所有下注的列表。

    mapping(bytes32 => Bet[]) private matchToBets;

第17和18行与到我们的oracle的连接有关。首先, 在boxingOracleAddr变量中, 存储oracle合同的地址(默认设置为零)。我们可以对oracle的地址进行硬编码, 但之后我们将永远无法对其进行更改。 (无法更改oracle地址可能是好事, 我们可以在第3部分中进行讨论)。下一行创建oracle接口的实例(在OracleInterface.sol中定义), 并将其存储在变量中。

    //boxing results oracle 
    address internal boxingOracleAddr = 0;
    OracleInterface internal boxingOracle = OracleInterface(boxingOracleAddr); 

如果你跳到第58行, 则会看到setOracleAddress函数, 在该函数中可以更改此oracle地址, 并在其中使用新地址重新实例化boxingOracle实例。

第21行以wei定义了我们的最小下注大小。当然, 这实际上是很小的数量, 只有0.000001以太。

    uint internal minimumBet = 1000000000000;

分别在第58和66行, 我们具有setOracleAddress和getOracleAddress函数。 setOracleAddress具有onlyOwner修饰符, 因为只有合同的所有者才能将oracle换成另一个oracle(可能不是一个好主意, 但我们将在第3部分中进行详细介绍)。另一方面, getOracleAddress函数是可公开调用的;任何人都可以看到正在使用的oracle。

function setOracleAddress(address _oracleAddress) external onlyOwner returns (bool) {...

function getOracleAddress() external view returns (address) { ....

在第72和79行, 我们分别具有getBettableMatches和getMatch函数。请注意, 这些只是将调用转发到oracle, 并返回结果。

function getBettableMatches() public view returns (bytes32[]) {...

function getMatch(bytes32 _matchId) public view returns ( ....

placeBet功能是非常重要的功能(第108行)。

function placeBet(bytes32 _matchId, uint8 _chosenWinner) public payable { ...

这一项的显着特征是应付款修饰符;我们一直在忙于讨论通用语言功能, 以至于我们还没有涉及能够与函数调用一起汇款的重要中心功能!基本上就是它了-它是一个可以接受一定数量的金钱以及所发送的任何其他参数和数据的函数。

我们在这里需要此功能, 因为用户可以在此同时定义他们将要进行的下注, 他们打算在该下注上赚多少钱并实际汇出钱。应付帐款修饰符启用该功能。在接受下注之前, 我们会进行一系列检查以确保下注的有效性。第111行的第一个检查是:

require(msg.value >= minimumBet, "Bet amount must be >= minimum bet");

发送的金额存储在msg.value中。假设所有支票都通过了第123行, 我们将把这笔款项转移到oracle的所有权中, 将该笔款项的所有权从用户那里转移到合同的拥有权中:

address(this).transfer(msg.value);

最后, 在第136行, 我们有一个测试/调试助手功能, 它将帮助我们知道合同是否连接到有效的oracle:

    function testOracleConnection() public view returns (bool) {
        return boxingOracle.testConnection(); 
    }

本文总结

实际上, 就本示例而言, 这是可行的。只是接受赌注。为了使示例足够简单以达到我们的目的, 故意将奖金分配和支付的功能以及一些其他逻辑省略了, 这仅仅是为了演示使用带有合同的预言片。当前, 另一个项目中存在更完整, 更复杂的逻辑, 这是该示例的扩展, 并且仍在开发中。

因此, 现在我们对代码库有了更好的了解, 并将其用作讨论Solidity提供的某些语言功能的工具和起点。这个由三部分组成的系列文章的主要目的是演示和讨论与Oracle的合同用法。本部分的目的是更好地理解此特定代码, 并将其用作了解Solidity和智能合约开发的某些功能的出发点。第三部分也是最后一部分的目的是讨论oracle使用的策略和哲学, 以及它在概念上如何适合智能合约模型。

进一步的可选步骤

我强烈鼓励希望学习更多的读者阅读并使用本代码。实施新功能。修复所有错误。实施未实现的功能(例如付款界面)。测试函数调用。修改它们并重新测试以查看会发生什么。添加一个web3前端。添加一种用于删除匹配项或修改其结果的功能(以防万一)。那取消的比赛呢?实现第二个oracle。当然, 合同可以自由使用任意数量的Oracle, 但是这会带来什么问题呢?玩的开心;这是一种很好的学习方式, 当你这样做(并从中获得乐趣)时, 你一定会保留所学的更多内容。

一个不完整的示例示例列表:

  • 在本地testnet中运行合同和oracle(按第1部分中所述, 在松露中), 并调用所有可调用函数和所有测试函数。
  • 添加功能, 用于在比赛结束时计算奖金并支付。
  • 添加了在平局时退还所有投注的功能。
  • 在比赛开始之前, 添加一项功能以请求退款或取消投注。
  • 添加一项功能, 以允许有时取消比赛(在这种情况下, 每个人都需要退款)。
  • 实施一项功能, 以确保用户下注时使用的预言片与将用于确定该比赛结果的预言片相同。
  • 实施另一个(第二个)甲骨文, 它具有一些与之相关的不同功能, 或者可能服务于拳击以外的一项运动(请注意, 参与者的计数和列表允许进行不同类型的运动, 因此实际上我们并不仅仅局限于拳击) 。
  • 实施getMostRecentMatch, 以便它实际上返回最近添加的匹配项, 或者返回就其发生时间而言最接近当前日期的匹配项。
  • 实现异常处理。

在熟悉了合同与甲骨文之间关系的机制之后, 在这个由三部分组成的系列文章的第三部分中, 我们将讨论该示例提出的一些战略, 设计和哲学问题。

赞(1)
未经允许不得转载:srcmini » 以太坊Oracle合同:统一代码功能(2)

评论 抢沙发

评论前必须登录!