阅读21分钟

etereum. Oracle合约:稳固代码功能

作为近二十年的全堆叠开发,约翰'使用使用C / C ++,.NET,SQL和JS的IOT,BlockChain,Web和移动项目工作。

在这三个分场的第一部分,我们经历了一个 为我们提供了一个简单的合同 - oracle对的小教程。描述了设置的机制和过程(用块粗酥),编译代码,部署到测试网络,运行和调试的代码;然而,这些代码的许多细节以手动波浪方式掩盖。所以现在,正如所承诺的那样,我们将参加一些独特的语言特征 坚固智能合同开发 这个特定的合同概况是独一无二的。虽然我们不能精心看看每一个细节(我会在进一步的研究中留给你,如果你愿意,我们将尝试击中代码最有趣,最有趣,最重要的功能。

为了促进此,我建议您打开自己的项目版本(如果有一个),或者有代码为参考。

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

以外的和坚固

稳定性不是唯一可用的智能合同开发语言,但我认为这足以说它是以为智能合同的最常见和最受欢迎的。当然,这是在撰写本文时具有最受欢迎的支持和信息的人。

关键的Etereum固体特征图

稳定性是面向对象和完成的。也就是说,您将迅速实现其内置(和完全有意的)限制,使智能合同编程感觉与普通的Let-Do-Do-offis-accking相比。

巩固版本

这是每个巩固码的第一行:

pragma solidity ^0.4.17;

您看到的版本号将与其稳固,仍处于青少年,仍然在其青少年中,正在改变和快速发展。版本0.4.17是我在我的例子中使用的版本;该出版物时的最新版本为0.4.25。

此时您正在阅读的最新版本可能完全不同。许多好的功能都是为了巩固的作品(或至少计划),我们将目前讨论。

这是不同的概述 坚固版本.

专家提示: 您还可以指定一系列版本(虽然我不经常看到这一点),如:

pragma solidity >=0.4.16 <0.6.0;

稳定性编程语言功能

稳定性具有许多语言特征,对大多数现代程序员来说都是熟悉的,以及一些不同的和(至少对我来说)不寻常。它据说是由C ++,Python和JavaScript的启发 - 所有这些都对我来说很熟悉,但稳定性似乎与任何这些语言都不同。

合同

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

contract BoxingOracle is Ownable {

随着类是面向对象语言的逻辑的基本单位,合同是稳定性逻辑的基本单位。足以简化它,现在说合同是“阶级”的巩固(面向对象的程序员,这是一个简单的飞跃)。

遗产

坚固的合同完全支持继承,它的工作原理;私人合同成员不会继承,而受保护和公共的成员则是。根据您的期望支持过载和多态性。

contract BoxingOracle is Ownable {

在上面的声明中,“是”关键字是表示继承。 boxingoracle继承自有。稳定性也支持多重继承。逗号分隔的类名列表表示多重继承,如下所示:

contract Child is ParentA, ParentB, ParentC {
…

虽然(在我看来),在构建继承模型时,这不是一个好主题,这是一个有趣的文章,关于所谓的稳定性 钻石问题.

枚举

enums坚持支持:

    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开始。如稳定性文档中所述,枚举值可转换为所有整数类型(例如,UINT,UINT16,UINT32,等等),但不允许使用隐式转换。这意味着必须明确地投射它们(例如,uint)。

巩固文件:枚举 枚举教程

结构

结构是另一种方式,如枚举,以创建用户定义的数据类型。所有C / C ++基础编码器和旧家伙都熟悉STRUCTS。来自第17行的结构示例 boxingoracle.sol.:

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

所有旧C程序员的注意事项: 稳定性的结构“包装”是一件事,但有一些规则和警告。不一定认为它与c中的工作相同;检查文档并了解您的情况,以确定包装是否会在特定情况下帮助您。

坚固的结构包装

一旦创建,可以在代码中寻址结构作为本机数据类型。以下是上面创建的结构类型的“实例化”的语法的示例:

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

稳定性的数据类型

这将我们带到了稳定性中的数据类型的基本主题。稳定性支持哪些数据类型?稳定性是静态类型的,并且在此写入数据类型时,必须明确声明并绑定变量。

国内固识中的数据类型

坚固数据类型

布尔斯

Boolean类型支持的名称 BOOL. 和价值观 真的 或者 错误的

数字类型

签名和无符号的INT8 / UINT8到INT256 / UINT256(分别为256位整数)的INT8 / UINT8(分别为8位整数),支持整数类型。 UINT256的uint速写是速记(同样int for int256)。

值得注意的是,浮点类型是 不是 支持的。为什么不?好吧,对于一件事,在处理货币价值时,众所周知,浮点变量是一个坏主意(当然一般),因为价值可以丢失到薄的空气中。醚值被表示在卫,这是1 / 1,000,000,000,000,000,000th醚的,并且必须具有足够的精确度以用于所有目的;您无法将以太换成较小的部分。

此时部分支持固定点值。根据巩固文件: “尚未通过稳定性完全支持固定点数。他们可以被宣告,但不能分配给或来自。“

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

笔记: 在大多数情况下,最好只使用uint,因为减少变量的大小(例如,对于UInt32),实际上可以提高天然气成本,而不是根据您所期望的降低它们。作为一般的经验法则,除非你确定你有一个有充分的理由,否则就是这样做。

字符串类型

稳定性的字符串数据类型是一个有趣的主题;根据您与谁交谈,您可能会得到不同的意见。稳定性有一个字符串数据类型,这是一个事实。我的意见可能是由大多数人分享,是它没有提供太多的功能。字符串解析,串联,替换,修剪,甚至计算字符串的长度:均未从字符串类型中获得的那些事情,因此它们是您的责任(如果您需要它们)。有些人使用bytes32代替字符串;这也可以完成。

有趣的文章关于固有字符串

我的意见:写自己的字符串类型并发布普遍使用可能是一个有趣的练习。

地址类型

独特可能是坚固的,我们有一个 地址 数据类型,专门针对Ethereum钱包或合同地址。它专门用于存储该特定大小的地址的20个字节值。此外,它还具有专门用于此类地址的类型成员。

地址 internal boxingOracleAddr = 0x145ca3e014aaf5dca488057592ee45305d9b3a22; 

地址数据类型

DateTime类型

例如,本身,没有稳定性的本机日期或DateTime类型,例如,在JavaScript中,才有。 (OH NO-SILVITY的声音越来越差,每个段落都更糟糕!?)日期被当地寻址为uint类型的时间戳(UINT256)。当块时间戳是UNIX样式时间戳时,它们通常以秒为单位而不是毫秒处理。在您发现自己有需要的人为可读日期的情况下,有各种原因,有开源库可用。您可能会注意到我在Boxingoracle中使用了一个: datelib.sol.。 OpenZeppelin还有日期公用事业以及许多其他类型的普通公用事业图书馆(我们将到达 图书馆 很快稳定的特征)。

专家提示: OpenZeppelin. 对于知识和预先写的通用代码来说,是一个很好的来源(但是当然不是唯一的好来源),可以帮助您提出建立合同。

映射

请注意,第11行 boxingoracle.sol. 定义一个名为a的东西 映射:

映射(bytes32 => uint) matchIdToIndex;

稳定性的映射是快速查找的特殊数据类型;基本上是查找表或类似于哈希可规的表,其中包含在区块链本身上的生命(当定义映射时,如这里,作为类成员)。在合同执行过程中,我们可以将数据添加到映射,类似于将数据添加到Hashtable,后来查找我们添加的值。请注意,在这种情况下,我们添加的数据被添加到区块链本身,因此它将持续存在。如果我们将其添加到今天在纽约的映射,从现在从伊斯坦布尔的某人读到了一周。

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

matchIdToIndex[id] = newIndex+1

从映射读取的示例,从51号线 boxingoracle.sol.:

uint index = matchIdToIndex[_matchId]; 

也可以从映射中删除项目。它在这个项目中没有使用,但它看起来像这样:

delete matchIdToIndex[_matchId];

返回值

正如您可能已经注意到的那样,稳定性可能对JavaScript有点肤浅相似,但它不会继承大部分JavaScript的类型和定义的松动。合同代码必须以相当严格和限制的方式定义(而这可能是一件好事,考虑用例)。考虑到这一点,请考虑从第40行的函数定义 boxingoracle.sol.

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

OK, so, let’s first just do a quick overview of what’s contained here. function marks it as a function. _getMatchIndex is the function name (the underscore is a convention that indicates a private member—we will discuss that later). It takes one argument, named _matchId (this time the underscore convention is used to denote function arguments) of type bytes32. The keyword private actually makes the member private in scope, view tells the compiler that this function does not modify any data on the blockchain, and finally: ~~~ solidity returns (uint) ~~~

This says that the function returns a uint (a function that returns void would simply have no returns clause here). Why is uint in parentheses? That’s because Solidity functions can and often do return 元组.

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

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

查看此返回条款!它返回一个,两个......七种不同的东西。好的,所以,这个函数将这些东西返回为元组。为什么?在开发过程中,您经常会发现自己需要返回结构(如果它是JavaScript,则可能需要返回JSON对象)。嗯,如这种写作(尽管在未来这可能会发生变化),稳定性不支持来自公共功能的返回结构。所以你必须返回元组。如果您是一个Python Guy,您可能会感到满意的元组。许多语言并不是真正支持它们,至少不是这样。

有关将元组返回为返回值的示例,请参阅第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); 

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

进口

第3行和4 boxingoracle.sol. are imports:

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

正如您可能期望的那样,这些是从与Boxingoracle.sol相同的合同项目文件夹中存在的代码文件导入定义。

修饰符

请注意,函数定义有一堆修饰符。首先,存在可见性:私人,公共,内部和外部 - 功能可见性.

Furthermore, you’ll see keywords pure and view. These indicate to the compiler what sort of changes the function will make, if any. This is important because such a thing is a factor in the final gas cost of running the function. See here for explanation: 巩固文件.

最后,我真正想要讨论,是自定义修饰符。看看61号线 boxingoracle.sol.:

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

Note the onlyOwner modifier just before the “public” keyword. This indicates that 只有所有者 of the contract may call this method! While very important, this is not a native feature of Solidity (though maybe it will be in the future). Actually, onlyOwner is an example of a custom modifier that we create ourselves, and use. Let’s have a look.

首先,修饰符在文件中定义 占有.sol.,您可以看到我们在第3行导入 boxingoracle.sol.:

import "./Ownable.sol"

Note that, in order to make use of the modifier, we’ve made BoxingOracle inherit from Ownable. Inside of 占有.sol.在第25行,我们可以找到“有权”合同内部修饰语的定义:

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

(顺便说一下,这一个人合同是从其中一个人获取的 OpenZeppelin.“公共合同”。)

请注意,此内容被声明为修饰符,表示我们可以使用它,以修改函数。请注意,修改器的肉是“需要”声明。要求陈述是一种像断言,但不能用于调试。如果Require语句的条件失败,则该函数将抛出异常。所以要解释这份“需要”声明:

require(msg.sender == owner);

我们可以说这意味着:

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

事实上,在稳定度下,更高版本,我们可以为该需要的错误添加错误消息:

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

最后,在好奇的阶段:

_; 

下划线为“此处”是“,执行修改后函数的完整内容”。因此,实际上,需要首先执行Require语句,然后是实际函数执行。因此,这就像预先挂起的那行逻辑到修改的函数。

当然,您可以使用修饰符进行更多的事情。检查文档: 译文.

坚固的文库

有一种稳定性的语言特征,称为 图书馆。我们在我们的项目中有一个例子 datelib.sol..

巩固图书馆实施!

这是一个更好地处理日期类型的库。它在第4行导入拳击镜:

import "./DateLib.sol";

它在第13行使用:

using DateLib for DateLib.DateTime;

DateLib.DateTime 是从Datelib合约(它作为成员暴露的结构)的结构;见第4行 datelib.sol.)我们在此声明我们“使用”某个数据类型的Datelib库。因此,该库中声明的方法和操作将适用于我们所说的数据类型。这就是图书馆用于稳定性的方式。

对于更明确的例子,请查看一些 OpenZeppelin.数字的图书馆,例如 保护。这些可以应用于本机(数字)稳定性数据类型(此处我们已将库应用于自定义数据类型),并且被广泛使用。

界面

与主流面向对象语言一样,支持接口。稳定性的接口被定义为合同,但函数主体省略了函数。有关接口定义的示例,请参阅 甲骨文Interface.sol.。在此示例中,该接口用作Oracle合同的待机,其内容驻留在单独的合同中,与单独的地址为单独的地址。

命名惯例

当然,命名约定不是全球规则;作为程序员,我们知道我们可以自由地遵循向我们吸引的编码和命名约定。另一方面,我们确实希望别人感到舒适的阅读和与我们的代码合作,因此有一定程度的标准化是可取的。

项目概况

因此,现在我们已经通过了所讨论的代码文件中存在的一些通用语言功能,我们可以开始更具体地看待该项目的代码本身。

因此,让我们澄清这个项目的目的,再次。该项目的目的是提供半现实(或伪拟真)演示和使用Oracle的智能合同示例。在它的心里,这只是一个责任调用另一个单独的合同。

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

  • 用户希望在拳击比赛上进行不同大小的赌注,为赌注支付金钱(以太),并在赢得胜利时收集奖金。
  • 用户通过智能合同使这些投注。 (在真实用例中,这将是一个带有web3前端的完整dapp;但我们只是在检查合同方面。)
  • 一个单独的智能合同 - Oracle-of由第三方维护。其工作是维护与当前状态的拳击比赛列表(未决,正在进行,完成等),如果完成,则赢家。
  • 主要合同获取oracle挂匹匹配的列表,并将这些与用户呈现为“bettable”匹配。
  • 主要合同接受赌注直到比赛开始。
  • 在决定匹配之后,主要合同根据简单算法向奖金和丢包进行奖励,削减自身,并根据要求支付奖金(输家只是失去整个股权)。

投注规则:

  • 有一个定义的,最小的下注(在魏中定义)。
  • 没有最大的赌注;用户可以打赌他们喜欢最低限度的金额。
  • 用户可以在比赛“正在进行中”这样的时间之前下注。

划分奖金的算法:

  • 收到的所有赌注都放入“锅中”。
  • 房子的锅中取出了一小部分。
  • 每个获胜者都获得了一部分比例的锅,与他们赌注的相对规模成正比。
  • 在决定匹配之后,一旦第一个用户请求结果,就会计算奖金。
  • 奖金由用户要求颁发。
  • 如果是绘制,没有人胜利 - 每个人都会得到他们的利益,房子没有削减。

Boxingoracle:Oracle合同

提供主要功能

甲骨文有两个接口,你可以说:一个提交给“所有者”和合同的维护者,并提交给公众;也就是说,合同消耗Oracle。维护者,它提供了将数据送入合同的功能,基本上从外界采取数据并将其放在区块链上。向公众提供对所述数据的只读访问。值得注意的是,合同本身限制了非业主编辑任何数据,而是对该数据的只读访问被公开授予而不限制。

给用户:

  • 列出所有匹配项
  • 列出待定匹配项
  • 获取特定匹配的详细信息
  • 获取特定匹配的状态和结果

给所有者:

  • 输入比赛
  • 更改匹配状态
  • 设置匹配的结果

用户和所有者接入元素的例证

用户的故事:

  • 5月9日宣布并确认了一个新的拳击比赛。
  • 我,合同的维护者(也许是我是一个着名的体育网络或新出口),将即将举行的匹配添加到Oracle在区块链上的数据,状态“待处理”。任何人或任何合同现在都可以查询并使用此数据。
  • 当比赛开始时,我将该匹配的状态设置为“正在进行”。
  • 当匹配结束时,我将匹配状态设置为“已完成”并修改匹配数据以表示获胜者。

甲骨文代码评论

此评价完全基于 boxingoracle.sol.;行号码引用该文件。

在第10行和11行,我们宣布我们的存储地点进行匹配:

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

matches 只是一个简单的数组来存储匹配实例,映射只是一个设施,用于将唯一的匹配ID(一个字节32值)映射到阵列中的索引,以便如果有人向我们掌握匹配的原始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 
    }

Line 61: Function addMatch is for use only by the contract owner; it allows for the addition of a new match to the stored data.

Line 80: Function declareOutcome allows the contract owner to set a match as “decided,” setting the participant who won.

第102-166行:以下功能全部可由公众呼叫。这是一般的只读数据,即公共公众:

  • Function getPendingMatches returns a list of IDs of all matches whose current state is “pending.”
  • Function getAllMatches returns a list of IDs of all matches.
  • Function getMatch returns the full details of a single match, specified by ID.

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

  • Function testConnection just tests that we are able to call the contract.
  • Function getAddress returns the address of this contract.
  • Function addTestData adds a bunch of test matches to the list of matches.

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

Boxingbets:客户合同

确定客户合同(投注合同)负责以及它不负责任是非常重要的。客户合同是 不是 负责维护真正的拳击赛列表或宣布其结果。我们“信任”(是的,我知道,有那个敏感的词哦 - 我们将在第3部分中讨论这个服务的Oracle。客户合同负责接受赌注。它对算法负责将奖金分开并根据匹配结果将其转移到获奖者的账户(从Oracle收到)。

此外,一切都是基于拉动的,没有事件或推动。合同从Oracle中拉动数据。合同从Oracle(响应于用户请求)中匹配的结果,并且合同计算奖金并响应用户请求来传输它们。

提供主要功能

  • 列出所有挂起的匹配项
  • 获取特定匹配的详细信息
  • 获取特定匹配的状态和结果
  • 赌注
  • 请求/接收奖金

客户代码评论

此评价完全基于 boxingbets.sol.;行号码引用该文件。

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

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

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

第13行将匹配的唯一ID映射到BET实例列表。有了这个,对于任何给定的匹配,我们可以获得为该匹配而制作的所有投注列表。

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

Lines 17 and 18 are related to the connection to our oracle. First, in the boxingOracleAddr variable, we store the address of the oracle contract (set to zero by default). We could hard-code the oracle’s address, but then we’d never be able to change it. (Not being able to change the oracle’s address could be a good or bad thing—we can discuss that in Part 3). The next line creates an instance of the oracle’s interface (which is defined in 甲骨文Interface.sol.)并将其存储在变量中。

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

If you jump ahead to line 58, you’ll see the setOracleAddress function, in which this oracle address can be changed, and in which the boxingOracle instance is re-instantiated with a new address.

第21行定义了我们的最小投注尺寸,在魏中。这当然实际上是非常少量的,只需0.000001乙醚。

    uint internal minimumBet = 1000000000000;

On lines 58 and 66 respectively, we have the setOracleAddress and the getOracleAddress functions. The setOracleAddress has the onlyOwner modifier because only the owner of the contract can switch out the oracle for another oracle (probably 不是 a good idea, but we will elaborate in Part 3). The getOracleAddress function, on the other hand, is publicly callable; anyone can see what oracle is being used.

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

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

On lines 72 and 79, we have the getBettableMatches and getMatch functions, respectively. Note that these are simply forwarding the calls on to the oracle, and returning the result.

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

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

The placeBet function is a very important one (line 108).

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

A striking feature of this one is the payable modifier; we’ve been so busy discussing general language features that we have not yet touched upon the centrally important feature of being able to send money along with function calls! That’s basically what it is—it’s a function which can accept an amount of money along with any other arguments and data sent.

We need this here because this is where the user simultaneously defines what bet they’re going to make, how much money they intend to have riding on that bet, and actually send the money. The payable modifier enables that. Before accepting the bet, we do a bunch of checks to ensure the validity of the bet. The first check on line 111 is:

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

The amount of money sent is stored in msg.value. Assuming that all of the checks pass, on line 123, we will transfer that amount into ownership of the oracle, taking ownership of that amount away from the user, and into the contract’s possession:

地址(this).transfer(msg.value);

最后,在第136行,我们有一个测试/调试帮助程序功能,可以帮助我们知道合同是否已连接到有效的Oracle:

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

包起来

这实际上就是这个例子;只是接受赌注。 拆除奖金并支付的功能以及一些其他逻辑的功能是故意遗漏的,以便使这个例子保持足够简单的目的,这只是为了展示与合同的甲骨文的使用。目前另一个项目中存在更完整和复杂的逻辑,这是此示例的扩展,仍在开发中。

所以现在我们对Codebase更好地了解,并将其用作跳跃点,讨论了稳定性提供的一些语言功能。这三部分系列的主要目的是展示和讨论与甲骨文合同的使用。本部分的目的是了解这一特定代码一点更好,并将其用作理解稳固和智能合同开发的一些特征。第三和最后一部分的目的是讨论Oracle使用的战略和哲学以及它如何概念上拟合到智能合同模型中。

进一步的可选步骤

我很鼓励希望了解更多信息的读者,以获取这笔代码并玩它。实施新功能。修复任何错误。实施未实现的功能(例如付款接口)。测试函数调用。修改它们并重新测试以查看会发生什么。添加Web3前端。添加设施以删除匹配或修改其结果(如果错误)。取消比赛怎么样?实施第二个Oracle。当然,合同可以自由地使用尽可能多的oracles,但这会产生什么问题?玩得开心;这是一个很好的学习方式,当你这样做时(并从中获得享受),你肯定会保留更多你学到的东西。

要尝试的样本,非全面清单:

  • 在本地TestNet中运行合同和Oracle(在Troofle中,如第1部分所述),并调用所有可调用的功能和所有测试功能。
  • 添加功能以计算奖金并在完成匹配时向其支付。
  • 添加功能以在绘制的情况下退还所有投注。
  • 在比赛开始之前,添加要申请退款或取消投注的功能。
  • 添加功能以允许匹配有时会被取消的事实(在这种情况下,每个人都需要退款)。
  • 实现一个功能,以保证用户在用户下注时到位的Oracle是相同的Oracle,用于确定该匹配的结果。
  • 实现另一个(第二)Oracle,它具有与之相关的一些不同的功能,或者可能提供除拳击之外的运动(请注意,参与者计数和列表允许不同类型的运动,因此我们实际上并没有仅限于拳击) 。
  • Implement getMostRecentMatch so that it actually returns either the most recently added match, or the match that’s closest to the current date in terms of when it will occur.
  • 实施异常处理。

一旦您熟悉合同与Oracle之间的关系的机制,在这三部分系列的第3部分中,我们将讨论此示例提出的一些战略,设计和哲学问题。