教程

Sandwich  2017年12月13号编辑了这个页面 ·共 11次修订

1.帐户和钱包

注意本教程适合在私有测试网上使用,但只需稍作修改即可在公共测试网上使用。

你会学到什么

您将学习如何创建和管理钱包及其钥匙,然后使用此钱包通过eosc与区块链进行交互。

本教程的用途

本教程适用于想了解钱包和账户管理的用户。它将尝试教你更多关于eosc 以及EOS上的钱包和账户如何相互交互。高级用户可能更适合查看参考命令

先决条件

  • 在你的系统上搭建并运行eosc和eos-walletd的复制文件。
  • 对命令行界面的基本了解

注:当应用在docker安装时,指令需要稍微修改。

1.1创建和管理钱包

打开您的终端并打开到EOS所在搭建的目录

这是与eosd和eos-walletd互动的命令行界面,这使我们更容易与eosc进行交互,

$ cd / path_to_eos / build / programs / eosc

你需要做的第一件事是创建一个钱包; 使用eosc的wallet create命令:

$ eosc wallet create     Creating wallet: defaultSave password to use in the future to unlock this wallet.Without password imported keys will not be retrievable.”A MASTER PASSWORD”

现在名为default的钱包已经在eos-walletd里面,并且已经返回了这个钱包的主密码。请务必将此密码保存在安全的地方。此密码用于解锁(解密)您的钱包文件。

这个钱包的文件名为default.wallet,位于  EOS目录的data-dir目录(或者–在eos-walletd启动时用data-dir参数指定的目录)

管理多个钱包和钱包名称

eosc能够管理多个钱包。每个单独的钱包受不同的钱包主密码保护。下面的例子创建了另一个钱包,并演示了如何使用-n参数来命名它。

现在确认钱包是用你选择的名字创建的。

 

注意在每个列出的钱包之后星号(*)是很重要的,这意味着相应的钱包已经解锁。为了方便起见,默认情况下,使用create wallet命令生成的钱包默认是没有锁起来的。

使用 wallet lock命令锁定第二个钱包

wallet list再次运行时,您会看到星号已经消失,这意味着钱包现在已被锁定。

解锁一个已命名的钱包需要调用wallet unlock命令,-n参数后面跟着解锁钱包的名称,然后在密码提示中输入钱包的主密码(你可以粘贴密码)。 继续,复制您创建的第二个钱包的主密码,执行下面的命令,当密码提示出现时,粘贴并按下enter键。你会得到一个确认信息。

$ eosc wallet unlock -n periwinkle

eosc会让你知道钱包被解锁了

Unlocked: ‘periwinkle’

注意:您也可以使用–password参数后跟主密码来跳过提示步骤,但是这会导致您的主密码在控制台历史记录中可见

现在检查你的进展

非常好,periwinkle钱包后面跟着一个星号,所以现在periwinkle钱包已经解锁了。

注意:使用钱包命令与default钱包交互不需要-n参数

现在重新启动eos-walletd,然后返回到您调用eosc的位置,然后运行以下命令

$ eosc wallet list
Wallets:

[]

有趣的是,钱包去了哪里?

钱包首先需要打开。因为你关了eos-walletd钱包,钱包没有打开。运行以下命令。

这样就好了。

注意:如果你想打开一个被命名的钱包,你会运行$ eosc wallet open -n periwinkle,去看会有什么效果吗?;

你会注意到在最后的响应中钱包是默认锁定的。现在解锁,您将在后续步骤中使用它。

运行wallet unlock命令,出现密码提示时,粘贴默认钱包的主密码。

$ eosc wallet unlock
Unlocked: 'default'

继续检查钱包是否已解锁

$ eosc wallet list

Wallets:[  “default *”]

钱包后面跟有一个星号,所以它被解锁了。非常棒。

您已经学会了如何创建多个钱包和如何在eosc中与他们互动。但是一个空的钱包对你没什么好处,有时间导入一些密钥。

1.2生成和导入EOS密钥

有几种方法可以生成一个EOS密钥对,但本教程将重点介绍eosc中的create key命令

生成两个密钥对

$ eosc create key
Private key:###
Public key: ###
$ eosc create key
Private key:###

Public key: ###

你现在有两个EOS密钥对。在这一点上,这些只是任意的密钥对,它们本身没有权限。

如果您按照之前的所有步骤操作,您的默认钱包现在应该已经打开并且已经解锁。

在下一个步骤中,我们执行wallet import两次,一一导入先前生成每个私钥。您现在需要将这些私钥导入您的default钱包。

$ eosc wallet import ${private_key_1}

然后再用第二个私钥

$ eosc wallet import ${private_key_2}

如果成功,则每次wallet import命令都使用与您的私钥对应的公钥进行响应,那您的控制台应该看起来像是.

$ eosc wallet import ${private_key_1}
imported private key for: ${public_key_1}
$ eosc wallet import ${private_key_2}

imported private key for: ${public_key_2}

我们可以通过调用wallet keys命令来检查哪些密钥被加载

钱包锁定时将保护这些密钥。访问锁定钱包中的密钥,需要创建钱包的时候eosc提供给您的主密码。由于钱包文件本身是加密的,因此备份您的密钥对不是必须的,如果要备份钱包文件强烈建议您备份在安全的地方。

1.3备份你的钱包

现在,您的钱包包含密钥,养成备份的习惯是很好的,以防一些可怕的事件导致的损失这个文件。比如说用闪存备份。如果没有密码,钱包文件就会被高熵加密,而且里面的密钥非常难以(所有合理的措施都不可能)访问。

你可以在data-dir 目录里面找到你的钱包文件。如果您–data-dir在启动eos时未指定参数,你可以在/path/to/eos/build/programs/eosd(eos的确切路径因系统而异)这个目录找到钱包文件。

$ cd / path_to_eos / build / programs / eosd && ls

blockchain   blocks   config.ini   default.wallet   periwinkle.wallet

在目录中你将看到两个文件,default.wallet和periwinkle.wallet。来吧,把它们保存在其他地方(熟能生巧!)

1.4创建一个帐户

如果您正在使用公共测试网络,那么您需要进行创世分配或faucet申请一个帐户,并进行必要的调整,(提示:应该用您的帐户替换inita

首先,让我们查一下create account命令和它的位置参数。

$ eosc create account inita $ {desired_account_name}  $ {public_key_1}  $ {public_key_2}

create account命令位置参数的细分

  • inita是为账户创建提供资金的账户名称,随后是新账户。
  • desired_account_name是您想要创建的帐户的名称
  • public_key_1和public_key_2是公钥,第一个被授予你的账户的所有者权限授,第二个授予你的账户的活动权限。

您以前生成了两个密钥对,您可以在您的控制台中向上翻命令,就像刚刚执行此命令时一样,或者如果您不屑上翻命令或已清除屏幕就执行wallet keys命令。

提醒一下,你的公钥是以EOS…开头的。在您将它们添加到一个权威机构中之前,上面的密钥是任意的;在你创建你的账户之前,您决定要使用哪个权威机构激活和持有是不重要的。

请注意,您的所有者权限的密钥等同于您的帐户的完全控制权,而您的活动权限密钥等同于您帐户中资金的完全访问权限。

用你刚刚学到的所有东西,把下面的命令的占位符替换掉,然后按回车。

$ eosc create account inita $ {desired_account_name}  $ {public_key_1}  $ {public_key_2}

你有没有收到一个在某处提到“权威”错误?不要担心,我故意让你这么做的。您遇到错误的原因是您没有 加载@inita帐户密钥。很抱歉,我忍不住这么做的。

inita密钥放在config.ini文件内,但为了您的方便,我已经继续拿来密钥并在下面提供了。你只需要执行提供的命令就可以,也许这可以弥补强行让你你出错的事?

$ eosc wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3

它会响应

imported private key for: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

现在,@inita帐户的私钥已经加载完毕,执行我把你带入陷阱之前你用心生成的 create account命令,然后按回车键。

如果一切顺利,eosc将返回一个带有交易ID的json对象,类似于以下内容

非常好!您现在在区块链上有一个帐户了。

继续前进,放松,你已经创建一个钱包、了解了一下钱包的工作原理、生成了密钥、将它们导入钱包并创建帐户了。

2.货币合约演练

目的

以下教程将引导用户完成货币合约示例,该合约示例可在此处的公共github存储库中 找到。

概述

货币合约将货币从一个帐户转到另一个帐户中,而每个本地用户的不同帐户余额保存在他们的本地范围内。

行动

目前,合约只开放了一个行动,它是:
currency_transfer:将一个货币的数量从一个账户转到另一个账户。

开始!

智能合约分为三个文件:

currency.hpp 头文件定义了合约的声明和结构
currency.cpp 合约逻辑和实现
currency.abi 用户交互的接口定义

头文件:currency.hpp

首先导入所有必要的库并定义您自己的命名空间,例如:

// Import necessary libraries

#include <eoslib/eos.hpp>   // Generic eos library, i.e. print, type, math, etc

#include <eoslib/token.hpp> // Token usage

#include <eoslib/db.hpp>    // Database access

namespace currency {

// Your code here

}

接下来添加一个货币代币; 这实际上是一个uin64_t包装器,用于为标准兼容代币消息检查的正确类型和下溢/溢出

typedef eosio::token<uint64_t,N(currency)> currency_tokens;

我们行动的结构如下:

另外,我们需要将余额存储在一个表中。该表定义如下:

using accounts = eosio::table<N(defaultscope),N(currency),N(account),account,uint64_t>;

  • 第一个模板参数定义了表的默认范围,即当数据存储到这个表中而没有指定范围时,它将退回到这个源帐户
  • 第二个模板参数定义表的所有者(即合约的名称)
  • 第三个模板参数定义表的名称
  • 第四个模板参数定义了它存储的结构(将在下一节中定义)
  • 第五个模板参数定义了表的密钥的数据类型

一旦表被定义,结构需要存储(在我们的例子中是“帐户”)也需要被定义。这是在另一个结构中完成的:

该结构包含一个构造函数和一个标准函数,用于比较一个空账户。

重要的是要注意,密钥是第一个变量,密钥的类型与上面表定义中定义的类型匹配(见第五个模板参数)。

为了把账户关联起来,我们添加一个访问器函数来访问所有者帐户信息,该函数返回存储在owner / TOKEN_NAME / account / account目录下的信息。把这个函数引用为头文件,可以给第三方访问用户的余额:

注意: accounts:get函数返回帐号所有者。如果账户不存在,则返回默认创建的账户。

源文件:currency.cpp

所有合约都具有上述的共同框架。每份合约都必须具备以上功能:

Init()函数在合约生命周期开始时被调用一次。它用来设置环境,以使合约正常运行。

apply(uint64_t code,uint64_t action_name)函数被用作消息接收器。每当消息发送到合约时,这个函数就是入口点; 这两个参数具有以下含义:

  • code:这是合约的名称
  • action_name:这是动作的名称

在货币合约的中,init()函数如下所示:

该合约首次运行时会检查货币账户是否已有表。在表中记录货币余额。如果合约中没有表,则生成一个新表且表初始总额为1000,000,000,这使货币合约成为1000,000,000货币单位的初始所有者。

消息接收器如下所示:

最好的做法是像上面的代码示例一样实现过滤器,以确保只有正确的消息才被处理以及过滤之后的消息才调用实际消息处理器。

请注意,current_message()函数在传递给特定处理器之前被调用,current_message()将合约收到的消息转换为struct  T.

实际消息处理器

货币的实际转账在以下方法进行:

代码是直截了当的,从源帐户中扣除货币并将对应货币添加到目标帐户。

require_notice函数是一个内联动作,可以将收到的消息转发到另一个帐户。在这个例子,转账消息被转发到源和目标账户。这是一个非常有用的功能,因为它使这些“通知帐户”在自己的功能里关联起来。

require_auth函数确保消息被以正确的方式签名。在这个例子,源账户的拥有者需要把交易签名,以便按照预期处理转账。

请注意,我们正在使用头文件中提供的get_account函数来获取正确的帐户对象。

由于我们使用的是代币,因此自动溢出和下溢断言将被支持到实际的减法和加法操作中。

最后,金额通过store_account函数更新。

Store_account

这个函数处理实际存储的余额:

关于这个有趣的事实是,这个current_account帐户在范围内保存的表如果是空的话会被删除。因为每当钱被转到一个不存在的帐户上时,表就被创建了。

因此,删除不必要的表格是一种资源节约机制,在编写任何智能合约时应作为最佳实践应用。

注:将上述代码示例与存储库中的实际代码进行比较时,请注意使用时将TOKEN_NAME另作命名,以便更轻松地重命名帐户名称。在上面的代码中,“TOKEN_NAME”已被帐户取代,以便阅读。

ABI文件:currency.abi

Abi(又名应用程序二进制接口)充当发送的消息和智能合约的二进制版本之间的接口。我们从一个包含以下对象的通用版本开始:

  • 结构:合约中动作/表使用的数据结构列表
  • 动作:合约中可用的动作列表
  • 表格:合约中可用的表列表

结构对象

从头文件中的信息可以创建大多数的ABI。因此,我们从结构开始。头文件中有两个结构:

这些结构变成以下ABI信息:

动作对象

动作对象对应相同。因此,我们在货币合约中有一个名为“转账”的动作,在ABI文件中看起来如下:

表对象

在头文件中,正在定义一个名为“account”表的索引:

eosio::table<N(defaultscope),N(currency),N(account),account,uint64_t>;

该表变成以下ABI对象:

将ABI文件整理好。

部署并运行

现在,所有三个文件(currency.hpp,currency.cpp,currency.abi)都可以从命令行部署:

$ eosc set contract currency currency.wast currency.abi

确保钱包已解锁并包含货币密钥。部署之后,可以通过命令行以如下方式触发合约动作:

$ eosc push message currency transfer ‘{“from”:“currency”,“to”:“tester”,“quantity”:50}’ -S currency -S tester -p currency@active

3.智能合约“Hello World”

为了简单起见,我们创建了一个工具eoscpp,可以用来辅助一个新的合约。为了这个工作,我们假设你已经安装了eosio / eos代码,并且$ {CMAKE_INSTALL_PREFIX} / bin在你的路径中。

$ eoscpp -n hello$ cd hello$ ls

以上将在“./hello”文件夹中创建一个带有三个合约文件的新的空项目:

hello.abi hello.hpp hello.cpp

我们来看看最简单的合约:

本合约实现了两个入口点,init和apply。它所做的只是记录传递的消息,并不做其他检查。只要块生产者允许,任何人任何时间都可以传递任何消息。在没有任何所需的签名的情况下,合约将因为消耗的带宽被计费。

您可以将此合约编译为WASM(.wast)的文本版本,如下所示:

$ eoscpp -o hello.wast hello.cpp

部署你的合约

现在你已经编译了你的应用程序,现在是部署它的时候了。这将要求您首先执行以下操作:

  1. 激活钱包插件启用eosd
  2. 至少为一个账户创建和导入密钥
  3. 保持你的钱包为解锁状态

假设您的钱包已解锁并且有${account}密钥,您现在可以使用以下命令将此合约上传到区块链:

如果您正在监控eosd进程的输出,您应该看到:

…] initt generated block #188249 @ 2017-09-13T22:00:24 with 0 trxs  0 pendingInit World!Init World!Init World!

你会注意到“初始世界!” 被执行3次,这不是出错。当区块链正在处理交易时,会发生以下情况:

  1. eosd收到一个新的交易
  • 创建一个临时会话
  • 尝试申请交易
  • 成功并打印“Init World
  • 或者失败并撤销更改(打印“Init World!”后可能会失败)
  1. eosd开始产生一个区块
  • 撤消所有待处理的状态
  • 随着区块的创建推送所有交易
  • 打印“Init World” 第二次
  • 搭建区块完成
  • 在创建区块时撤销所有临时更改
  1. eosd将生成的区块按照从网络接收的方式推送
  • 打印“Init World” 第三次

此时您的合约已准备好开始接收消息。由于默认消息处理程序接受所有消息,我们可以发送任何我们想要的。让我们尝试发送一个空的消息:

$ eosc push message ${account} hello ‘”abcd”‘ –scope ${account}

该命令将用十六进制字符串“abcd”表示的二进制数据发送消息“hello”。请注意,稍后我们将演示如何定义ABI,以便您可以用十分易读的JSON对象替换十六进制字符串。现在我们只想演示如何将 “hello” 消息类型发送。

结果是:

如果你在eosd中跟踪,那么你应该看到的屏幕滚动输出下面的内容:

Hello World:$ {account} – > hello

Hello World:$ {account} – > hello

Hello World:$ {account} – > hello

您的合约再一次执行和撤消两次,然后这三次应用为生成的区块的一部分。

消息名称限制

消息类型实际上是base32编码的64位整数。这意味着它们前12名字符仅限于字符a-z,1-5和’.’。如果有第13个字符,则它的前16个字符被限制在(’.’和a-p)之间。

ABI – 应用程序二进制接口

ABI是关于如何在它们的JSON和二进制表示之间转换用户动作的基于JSON的描述。ABI还介绍了如何将数据库状态转换为JSON或如何把JSON转为数据库状态。一旦使用ABI转化了合约,开发人员和用户就可以通过JSON无缝地与您的合约进行交互。

我们正在开发能够自动从C ++源代码生成ABI的工具,但是目前您可能需要手动生成ABI。

下面是基本的合约ABI的例子:

你会注意到这个ABI定义了一个transfer类型的动作transfer。这告诉EOS.IO,当看到有效负荷${account}->transfer消息就是是transfer的类型消息。Transfer类型在structs数组对象的name字段设置为”transfer”定义。

它有几个领域,包括from,to和quantity。这些领域有coresponding类型account_name,account_name和uint64。 account_name被定义为types数组中的的name的类型 ,这是一个内置类型,用于将uint64_t类型编制为base32类型(例如,帐户名称)。

现在我们已经查看了基本定义的ABI,我们可以创建一个消息调用transfer:

如果你观察你的eosd输出应该看到:

Hello World: ${account}->transfer

Hello World: ${account}->transfer

Hello World: ${account}->transfer

处理转账消息的参数

根据ABI,转账消息的格式为:

我们也知道account_name – > name – > uint64,这意味着消息的二进制表示与以下内容相同:

EOS.IO C API通过Message API提供对消息有有效负荷的访问:

uint32_t message_size();uint32_t read_message( void* msg, uint32_t msglen );

让我们修改hello.cpp来打印出消息的内容:

然后我们可以重新编译并部署它:

eoscpp -o hello.wast hello.cpp eosc set contract ${account} hello.wast hello.abi

由于重新部署,eosd会再次调用init()

Init World!Init World!Init World!

然后我们可以执行转账:

在eosd我们应该看到下面的输出:

Hello World: ${account}->transferTransfer 50 from currency to initaHello World: ${account}->transferTransfer 50 from currency to initaHello World: ${account}->transferTransfer 50 from currency to inita

使用C ++ API读取消息

到目前为止,我们使用了C API,因为它是EOS.IO直接向WASM虚拟机暴露的最底层API。幸运的是,eoslib提供了一个更高级的API,去除了大部分底层的东西。

我们可以更新hello.cpp变得更简洁如下:

您会注意到我们更新了transfer结构去直接使用eosio::name类型,然后将涉及read _Message的检查压缩为对current-Message的单个调用。

编译并上传之后,您应该得到与C版本相同的结果。

要求发送者转账权限

任何合约的最常见的要求之一是定义谁可以执行的行动。在固化传输的情况下,我们希望要求由from参数定义的帐户在消息上签名。

EOS.IO软件将负责执行和验证签名,只需获得必要的授权即可。

在搭建和部署之后,我们可以尝试再次转账:

如果您查看eosd,您将看到以下内容:

 

这表明它试图应用你的交易,打印了初始的“Hello World”,然后在eosio::require_auth找不到帐户授权时中止initb。

我们可以通过告诉eosc添加所需的权限来解决这个问题:

eosc push message ${account} transfer ‘{“from”:”initb”,”to”:”inita”,”quantity”:50}’ –scope ${account} –permission initb@active

该–permission命令定义了帐户和权限级别,这个例子中,我们使用默认的active 权限。

这一次转账应该像我们之前看到的那样工作。

在错误中中止消息

合约开发的很大一部分是验证前提条件,使得转账的quantity(数量)大于0。如果用户试图执行无效操作,则合约必须中止,并且所做的任何更改都会自动恢复

我们现在可以编译、部署并尝试执行quantity 为0的转账:

$ eoscpp -o hello.wast hello.cpp

$ eosc set contract ${account} hello.wast hello.abi

$ eosc push message ${account} transfer ‘{“from”:”initb”,”to”:”inita”,”quantity”:0}’ –scope initc –permission initb@active

3071182ms thread-0   main.cpp:851                  main                 ] Failed with error: 10 assert_exception: Assert Exception

status_code == 200: Error

: 10 assert_exception: Assert Exception

test: assertion failed: Must transfer a quantity greater than 0

4.井字游戏

目的

下面的教程将指导用户搭建一个玩家vs玩家合约游戏。我们将使用tic tac toe游戏来演示这个。本教程的最终结果可以在这里找到。

假设

对于这个游戏,我们正在使用一个标准的3×3井字棋牌。玩家分为两个角色:主人挑战者。主人总是走第一步。每对玩家最多只能同时玩两场,第一场玩家成为主人,第二场玩家成为主人。

不是使用o和x作为传统的井字游戏。我们1用来表示主人的移动,2表示挑战者移动,0表示空单元格。此外,我们将使用一维数组来存储游戏卓。因此:

0,0 1,0 2,0
0,0 Ø X
0,1 X
0,2 X Ø Ø

假设x是主人,上面的游戏板与[0, 2, 1, 0, 1, 0, 1, 2, 2]相等

行动

用户将有以下行动来与本合约交互:

  • 创造:创造一个新的游戏
  • 重新启动:重新启动一个现有的游戏,允许主机/挑战者这样做
  • 关闭:关闭现有游戏,释放用于存储游戏的存储空间,只允许主人执行此操作
  • 移动:做一个移动

合约帐户

对于下面的指南,我们将把合约创建为一个名为tic.tac.toe的游戏。如果tic.tac.toe帐号名称已被占用,您也可以使用其他帐号名,将代码中的任何tic.tac.toe内容替换为您的帐号名称。如果您还没有创建帐户,请先创建帐户。

$ eosc create account ${creator_name} ${contract_account_name} ${contract_pub_owner_key} ${contract_pub_active_key} –permission ${creator_name}@active# e.g. $ eosc create account inita tic.tac.toe  EOS4toFS3YXEQCkuuw1aqDLrtHim86Gz9u3hBdcBw5KNPZcursVHq EOS7d9A3uLe6As66jzN8j44TXJUqJSK3bFjjEEqR4oTvNAB3iM9SA –permission inita@active

请确保您的钱包已解锁,并且且钱包中创建者的私人有效密钥已导入,否则上述命令将失败。

开始!

我们将在这里创建三个文件:

  • tic_tac_toe.hpp =>定义合约结构的头文件
  • tic_tac_toe.cpp =>合约的主要逻辑
  • tic_tac_toe.abi =>用户与合约交互的接口

定义结构

我们首先从头文件开始,定义合约的结构。打开tic_tac_toe.hpp并从下面的示例开始

游戏桌

对于这个合约,我们需要有一个存储游戏列表的表。我们来定义它:

注意:如果您将合约上传到其他非tic.tac.toe账户,请把tic.tac.toe用您的账户名称替换

  • 第一个模板参数定义了表的默认范围,即当数据存储到这个表中而没有指定范围时,它将回退到这个帐户。
  • 第二个模板参数定义了对此表具有写入权限的帐户,在这个示例中,它写的是合约帐户名称
  • 第三个模板参数定义表的名称
  • 第四个模板参数定义了它存储的结构(将在下一节中定义)
  • 第五个模板参数定义了表的密钥的数据类型

游戏结构

我们来定义游戏的结构。确保此结构定义出现在代码中的表定义之前。

请记住,在前面的表定义中,我们将声明uint64_t为表的密钥的数据类型。因此,对于上述游戏结构,第一个sizeof(uint64_t)字节将被视为表的密钥。顺便说一句,account_name只是uint64_t的一个别名。

动作结构

创建

要创建游戏,我们需要主人帐户名称和挑战者的帐户名称

重新开始

要重新开始游戏,我们需要主人帐户名称和挑战者的帐户名称来识别游戏。此外,我们需要指定谁想要重新启动游戏,让我们可以验证是否提供了正确的签名。

关闭

要关闭游戏,我们需要主人帐户名称和挑战者的帐户名称来识别游戏。

移动

要移动,我们需要主人帐户名称和挑战者的帐户名称来识别游戏。此外,我们需要指定谁做这个动作和他正在做的动作。

你可以在这里找到最后的tic_tac_toe.hpp文件

主要

让我们打开tic_tac_toe.cpp并设置样板

消息处理器

我们希望tic_tac_toe合约只对消息发送tic.tac.toe账户的作出响应,并根据行动的类型作出不同的反应。让我们在apply函数中设置消息过滤器:

请注意,我们在将它传递给特定处理程序之前使用调用current_message<T>()函数, current_message<T>()将合约收到的消息转换成struct T。

注意:如果您要将此合约部署到除tic.tac.toe之外的帐户,请把tic.tac.toe用您的帐户名称替换。

为了整洁,我们将在namespace tic_tac_toe里面封装消息处理器:

创建消息处理器

对于创建消息处理器,我们希望:

  1. 确保消息具有来自主人的签名
  2. 确保游戏不是由同一个玩家玩的
  3. 确保游戏当下不存在
  4. 将新创建的游戏存储到数据库中

重新启动消息处理器

对于创建消息处理器,我们希望:

  1. 确保消息具有来自主人/挑战者的签名
  2. 确保游戏存在
  3. 确保重新启动操作由主人/挑战者完成
  4. 重置游戏
  5. 将更新的游戏存储到数据库

关闭消息处理器

对于创建消息处理器,我们希望:

  1. 确保消息具有来自主人的签名
  2. 确保游戏存在
  3. 从数据库中删除游戏

移动消息处理器

对于移动消息处理器,我们希望:

  1. 确保消息具有来自主人/挑战者的签名
  2. 确保游戏存在
  3. 确保游戏尚未完成
  4. 确保移动操作由主人/挑战者完成
  5. 确保这是轮到的正确的用户
  6. 验证移动是否有效
  7. 更新移动后的棋盘
  8. 将move_turn更改为其他玩家
  9. 找到胜利者
  10. 将更新的游戏存储到数据库

移动验证

有效的移动被定义为完成移动到游戏桌上的一个空格的动作:

获得优胜者

获胜者被定义为第一个成功将他们的三个标记放置在水平线,垂直线或对角线上的玩家。

你可以在这里看到最后的tic_tac_toe.cpp

创建ABI

Abi(又名应用程序二进制接口)在这里是需要的,所以合约可以理解你以二进制形式发送的消息。让我们打开tic_tac_toe.abi并在这里定义引用:

  • 结构:合约中动作/表使用的数据结构列表
  • 操作:合约中可用的操作列表
  • 表格:合约中可用的表列表

Table ABI

请记住,在tic_tac_toe.hpp中,我们创建一个单独索引的i64表,称为游戏。它保存game结构和使用challenger作为密钥,challenger的数据类型是account_name。因此,abi将是:

动作ABI

对于这些动作,我们在actions里面定义里面的动作和structs里面定义的动作的结构。

部署!

现在所有的三个文件(tic_tac_toe.hpp,tic_tac_toe.cpp,tic_tac_toe.abi)都准备好了。是时间开始部署!

$ eosc set tic.tac.toe tic_tac_toe.wast tic_tac_toe.abi

确保您的钱包已解锁,并且已把tic.tac.toe密钥导入。如果您要将合约上传到除tic.tac.toe外另一个帐户,请替换tic.tac.toe为您的帐户名称,并确保您的钱包中有该帐户的密钥

玩!

部署和交易确认后,合约已经在区块链中可用。你现在可以玩了!

创建

$ eosc push message tic.tac.toe create ‘ {“challenger”:“inita”,“host”:“initb”} ‘- S initb -S tic.tac.toe -p initb @ active

移动

$ eosc push message tic.tac.toe move ‘ {“challenger”:“inita”,“host”:“initb”,“by”:“initb”,“movement”:{“row”:0, :0}} ‘- S initb -S tic.tac.toe -p initb @ active$ eosc push message tic.tac.toe move ‘ {“challenger”:“inita”,“host”:“initb”,“by”:“inita”,“movement”:{“row”:1, :1}} ‘- S initb -S tic.tac.toe -p inita @ active

重新开始

$ eosc push message tic.tac.toe restart ‘ {“challenger”:“inita”,“host”:“initb”,“by”:“initb”} ‘- S initb -S tic.tac.toe -p initb  @ active

$ eosc push message tic.tac.toe close ‘ {“challenger”:“inita”,“host”:“initb”} ‘- S initb -S tic.tac.toe -p initb @ active

查看游戏状态