Tutorial Getting Started With Contracts_zh.md - wilddylan/eos GitHub Wiki
EOSIO开发教程
本教程的目的是演示如何设置一个本地区块链,可以用来试验智能合约。本教程的第一部分将关注以下内容:
- 启动节点
- 创建钱包
- 创建账户
- 部署智能合约
- 与智能合约交互
本教程的第二部分将介绍如何创建和部署自己的智能合约。
本教程假设您已经安装了 EOSIO,而 nodeos
和 cleos
在您的路径中。
开始一个单节点区块链
仅一行代码就可以开启单节点区块链:
$ nodeos -e -p eosio --plugin eosio::wallet_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::account_history_api_plugin
...
eosio generated block 046b9984... #101527 @ 2018-04-01T14:24:58.000 with 0 trxs
eosio generated block 5e527ee2... #101528 @ 2018-04-01T14:24:58.500 with 0 trxs
这个命令设置了很多标记并加载了一些可选的插件,这是我们接下来的教程所需要的。假设一切正常,您应该每0.5秒就看到一个块生成消息。
eosio generated block 046b9984... #101527 @ 2018-04-01T14:24:58.000 with 0 trxs
这意味着您的本地区块链是实时的,生成块,并准备使用。
有关 nodeos
参数的更多信息,请使用:
nodeos --help
创建一个钱包
钱包是在区块链上授权操作所需的私有密钥的存储库。这些密钥存储在磁盘上,为您生成的密码进行加密。这个密码应该存储在一个安全的密码管理器中。
$ cleos wallet create
Creating wallet: default
Save password to use in the future to unlock this wallet.
Without password imported keys will not be retrievable.
"PW5JuBXoXJ8JHiCTXfXcYuJabjF9f9UNNqHJjqDVY7igVffe3pXub"
为了这个简单的开发环境,您的钱包是由您的本地 nodeos
通过 eosio::wallet_api_plugin
管理的,我们在启动 nodeos
时启用了它。当你重启 nodeos
时,你必须先解锁你的钱包,然后才能使用钥匙。
$ cleos wallet unlock --password PW5JuBXoXJ8JHiCTXfXcYuJabjF9f9UNNqHJjqDVY7igVffe3pXub
Unlocked: default
在命令行上直接使用密码是不安全的,因为它会被记录到您的 bash$ history
中,所以您也可以在交互模式下解锁:
$ cleos wallet unlock
password:
出于安全考虑,当你不使用时,通常最好把钱包锁起来。在不关机的情况下锁定你的钱包,你可以这样做:
$ cleos wallet lock
Locked: default
在本教程的剩余部分中,您将需要解锁您的钱包。
所有新的区块链从一个主键开始,这是唯一的初始账户 eosio
。要与区块链交互,您需要将这个初始帐户的私钥导入到您的钱包中。
将 eosio
帐户的主键导入到您的钱包中。主键可以在配置中找到。为 nodeos
配置文件夹中的 ini 文件。在本例中,使用了默认的配置文件夹。在 Linux 系统上,绝对地址为 ~/.local/share/eosio/nodeos/config
,在 Mac OS 上,绝对地址为 ~/Library/Application Support/eosio/nodeos/config
。
$ cleos wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
imported private key for: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
加载 Bios 合约
既然我们已经有了一个带有 eosio 账户密钥的钱包,我们可以设置一个默认的系统合约。为了开发的目的,默认的 eosio。可以使用 bios 智能合约。该合约使您能够直接控制其他帐户的资源分配,并访问其他特权API调用。在公共区块链中,该合约将管理令牌的 staking 和 unstaking,以保留 CPU 和网络活动的带宽,以及合约的内存。
eosio.bios
合约 可以在 contracts/eosio.bios
EOSIO 源码目录下发现。默认是从源码位置执行,当然你也可以指定执行 ${EOSIO_SOURCE}/build/contracts/eosio.bios
。
$ cleos set contract eosio build/contracts/eosio.bios -p eosio
Reading WAST...
Assembling WASM...
Publishing contract...
executed transaction: 414cf0dc7740d22474992779b2416b0eabdbc91522c16521307dd682051af083 4068 bytes 10000 cycles
# eosio <= eosio::setcode {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001ab011960037f7e7f0060057f7e7e7e...
# eosio <= eosio::setabi {"account":"eosio","abi":{"types":[],"structs":[{"name":"set_account_limits","base":"","fields":[{"n...
这个命令序列的结果是,cleos 生成了一个带有两个动作的事务,eosio::setcode
和 eosio::setabi
。
代码定义了合约的运行方式,abi 描述了如何在参数的二进制和 json 表示之间进行转换。虽然 abi 在技术上是可选的,但是所有的 EOSIO 工具都依赖于它的易用性。
当您执行事务时,您将看到如下输出:
executed transaction: 414cf0dc7740d22474992779b2416b0eabdbc91522c16521307dd682051af083 4068 bytes 10000 cycles
# eosio <= eosio::setcode {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001ab011960037f7e7f0060057f7e7e7e...
# eosio <= eosio::setabi {"account":"eosio","abi":{"types":[],"structs":[{"name":"set_account_limits","base":"","fields":[{"n...
可以被理解为:这个被 eosio
定义的动作 setcode
被 eosio
合约携带一些参数执行了。
# ${executor} <= ${contract}:${action} ${args...}
> console output from this execution, if any
正如我们将会看到的,操作可以通过多个合约来处理。
这个调用的最后一个参数是 -p eosio
。这告诉 cleos 用 eosio 帐户的活动权限来签署此操作,即,使用我们之前导入的eosio帐户的私钥签署该操作。
创建账户
现在我们已经建立了基本的系统契约,我们可以开始创建我们自己的帐户。我们将创建两个帐户,用户和测试人员,我们需要将一个键与每个帐户关联。在本例中,两个帐户都使用相同的密钥。 为此,我们首先为帐户生成一个密钥。
$ cleos create key
Private key: 5Jmsawgsp1tQ3GD6JyGCwy1dcvqKZgX6ugMVMdjirx85iv5VyPR
Public key: EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
然后我们把密钥导入我们的钱包:
$ cleos wallet import 5Jmsawgsp1tQ3GD6JyGCwy1dcvqKZgX6ugMVMdjirx85iv5VyPR
imported private key for: EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
注意: 确保使用cleos命令生成的实际键值,而不是上面示例中所示的键值!密钥不会自动添加到钱包中,因此跳过这一步可能会导致对您的帐户失去控制。
创建两个用户帐户
接下来,我们将创建两个帐户,user
和 tester
,使用我们在上面创建和导入的密钥。
$ cleos create account eosio user EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
executed transaction: 8aedb926cc1ca31642ada8daf4350833c95cbe98b869230f44da76d70f6d6242 364 bytes 1000 cycles
# eosio <= eosio::newaccount {"creator":"eosio","name":"user","owner":{"threshold":1,"keys":[{"key":"EOS7ijWCBmoXBi3CgtK7DJxentZZ...
$ cleos create account eosio tester EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
executed transaction: 414cf0dc7740d22474992779b2416b0eabdbc91522c16521307dd682051af083 366 bytes 1000 cycles
# eosio <= eosio::newaccount {"creator":"eosio","name":"tester","owner":{"threshold":1,"keys":[{"key":"EOS7ijWCBmoXBi3CgtK7DJxentZZ...
注意: create account
命令需要两个密钥,一个用于OwnerKey(在生产环境中应该保持高度安全),另一个用于ActiveKey。在本教程示例中,两者都使用相同的密钥。
因为我们在启动节点时配置了该插件 eosio::account_history_api_plugin
,我们可以查询所有由我们的密钥控制的帐户:
$ cleos get accounts EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
{
"account_names": [
"tester",
"user"
]
}
创建 token 智能合约
在这个阶段区块链没有做太多,所以让我们部署 eosio.token
。该合约允许在同一合约上创建许多不同的 token
,由不同的用户管理。
在部署 token 合约之前,我们必须创建一个帐户来部署它。
$ cleos create account eosio eosio.token EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
...
在该目录下可以找到合约:${EOSIO_SOURCE}/build/contracts/eosio.token
。
$ cleos set contract eosio.token build/contracts/eosio.token -p eosio.token
Reading WAST...
Assembling WASM...
Publishing contract...
executed transaction: 528bdbce1181dc5fd72a24e4181e6587dace8ab43b2d7ac9b22b2017992a07ad 8708 bytes 10000 cycles
# eosio <= eosio::setcode {"account":"eosio.token","vmtype":0,"vmversion":0,"code":"0061736d0100000001ce011d60067f7e7f7f7f7f00...
# eosio <= eosio::setabi {"account":"eosio.token","abi":{"types":[],"structs":[{"name":"transfer","base":"","fields":[{"name"...
创建 EOS Token
你可以查看相关 eosio.token
的接口,定义在 contracts/eosio.token/eosio.token.hpp
中:
void create( account_name issuer,
asset maximum_supply,
uint8_t can_freeze,
uint8_t can_recall,
uint8_t can_whitelist );
void issue( account_name to, asset quantity, string memo );
void transfer( account_name from,
account_name to,
asset quantity,
string memo );
为了创建一个新的令牌,我们必须用正确的参数调用 create(…)
动作。这个命令将使用最大供应的符号来唯一地从其他令牌标识这个令牌。发行者将是有权要求发行或执行其他行为的人,如冻结、召回和白名单所有者。
使用位置参数(数组,按照参数顺序传入)来调用此方法的简明方法:
$ cleos push action eosio.token create '[ "eosio", "1000000000.0000 EOS", 0, 0, 0]' -p eosio.token
executed transaction: 0e49a421f6e75f4c5e09dd738a02d3f51bd18a0cf31894f68d335cd70d9c0e12 260 bytes 1000 cycles
# eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"1000000000.0000 EOS","can_freeze":0,"can_recall":0,"can_whitelis...
或者,更详细的方法( Key:value 形式)调用此方法:
$ cleos push action eosio.token create '{"issuer":"eosio", "maximum_supply":"1000000000.0000 EOS", "can_freeze":0, "can_recall":0, "can_whitelist":0}' -p eosio.token
executed transaction: 0e49a421f6e75f4c5e09dd738a02d3f51bd18a0cf31894f68d335cd70d9c0e12 260 bytes 1000 cycles
# eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"1000000000.0000 EOS","can_freeze":0,"can_recall":0,"can_whitelis...
这个命令创建了一个新的 EOS
token,4位小数点,总计 1000000000.0000 EOS。
为了创建这个 token,我们需要 eosio 的许可。eos.token
,因为它 “拥有” 符号名称空间 (例如: “EOS” )。本合同的未来版本可能允许其他各方自动购买符号名称。因此我们必须通过 -p eosio.Token
授权此调用。
User
发出 tokens
向帐户 现在我们已经创建了 tokens,发行者可以向我们之前创建的帐户用户发出新的 tokens。我们将使用位置调用约定(vs命名的args):
$ cleos push action eosio.token issue '[ "user", "100.0000 EOS", "memo" ]' -p eosio
executed transaction: 822a607a9196112831ecc2dc14ffb1722634f1749f3ac18b73ffacd41160b019 268 bytes 1000 cycles
# eosio.token <= eosio.token::issue {"to":"user","quantity":"100.0000 EOS","memo":"memo"}
>> issue
# eosio.token <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 EOS","memo":"memo"}
>> transfer
# eosio <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 EOS","memo":"memo"}
# user <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 EOS","memo":"memo"}
这一次输出包含几种不同的操作: 一次发放与三次交易. 虽然我们签署的唯一行是 发放 (issue)
, 发放
行动执行了两次 内联转移
通知发件人和接收者帐户。输出指示所有被调用的操作处理程序,调用它们的顺序,以及操作是否生成任何输出。
从技术上讲,eosio.token
可以跳过内联传输,并选择直接修改余额。然而,在这种情况下,eosio.token
遵循我们的令牌约定,要求所有帐户余额都可以通过引用它们的传输操作的和来派生。它还要求通知资金的发件人和接收者,以便他们能够自动处理存款和取款。如果您希望看到广播的实际事务,您可以使用 -d -j
选项表示 不广播
和 返回事务为json
。
$ cleos push action eosio.token issue '["user", "100.0000 EOS", "memo"]' -p eosio -d -j
{
"expiration": "2018-04-01T15:20:44",
"region": 0,
"ref_block_num": 42580,
"ref_block_prefix": 3987474256,
"net_usage_words": 21,
"kcpu_usage": 1000,
"delay_sec": 0,
"context_free_actions": [],
"actions": [{
"account": "eosio.token",
"name": "issue",
"authorization": [{
"actor": "eosio",
"permission": "active"
}
],
"data": "00000000007015d640420f000000000004454f5300000000046d656d6f"
}
],
"signatures": [
"EOSJzPywCKsgBitRh9kxFNeMJc8BeD6QZLagtXzmdS2ib5gKTeELiVxXvcnrdRUiY3ExP9saVkdkzvUNyRZSXj2CLJnj7U42H"
],
"context_free_data": []
}
转义 Tokens 给 "Tester"
既然帐户 user
已经有了令牌,我们将把一些帐户转到 tester
账户下。我们指示用户使用权限参数 -p user
授权此操作。
$ cleos push action eosio.token transfer '[ "user", "tester", "25.0000 EOS", "m" ]' -p user
executed transaction: 06d0a99652c11637230d08a207520bf38066b8817ef7cafaab2f0344aafd7018 268 bytes 1000 cycles
# eosio.token <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 EOS","memo":"m"}
>> transfer
# user <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 EOS","memo":"m"}
# tester <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 EOS","memo":"m"}
Hello World Contract
我们将创建我们的第一个 hello world
智能合约。创建一个名为 hello
的新文件夹,cd
进入文件夹,然后创建一个文件 hello.cpp
,输入以下内容(这里请使用最新的master分支):
hello/hello.cpp
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
using namespace eosio;
class hello : public eosio::contract {
public:
using contract::contract;
/// @abi action
void hi( account_name user ) {
print( "Hello, ", name{user} );
}
};
EOSIO_ABI( hello, (hi) )
您可以将代码编译成 web assmebly (.wast):
$ eosiocpp -o hello.wast hello.cpp
注意: 编译器可能会生成警告。这些可以被安全地忽略。
现在生成 abi 文件:
$ eosiocpp -g hello.abi hello.cpp
Generated hello.abi
创建帐户并上传智能合约:
$ cleos create account eosio hello.code EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
...
$ cleos set contract hello.code ../hello -p hello.code
...
现在我们可以运行智能合约了:
$ cleos push action hello.code hi '["user"]' -p user
executed transaction: 4c10c1426c16b1656e802f3302677594731b380b18a44851d38e8b5275072857 244 bytes 1000 cycles
# hello.code <= hello.code::hi {"user":"user"}
>> Hello, user
此时智能合约允许任何人授权,我们也可以说:
$ cleos push action hello.code hi '["user"]' -p tester
executed transaction: 28d92256c8ffd8b0255be324e4596b7c745f50f85722d0c4400471bc184b9a16 244 bytes 1000 cycles
# hello.code <= hello.code::hi {"user":"user"}
>> Hello, user
在这种情况下,tester
是授权它的人,而用户只是一个参数。如果我们想要我们的联系人对我们所说的 hi
用户进行身份验证,那么我们需要修改智能合约来要求认证。
在 hello.cpp
中修改 hi()
函数。cpp如下:
void hi( account_name user ) {
require_auth( user );
print( "Hello, ", name{user} );
}
重复上述步骤来编译 wast 文件并生成 abi,然后再次设置该契约来部署更新。 现在,如果我们试图不匹配用户和权限,智能合约会抛出一个错误:
$ cleos push action hello.code hi '["tester"]' -p user
Error 3030001: missing required authority
Ensure that you have the related authority inside your transaction!;
If you are currently using 'cleos push action' command, try to add the relevant authority using -p option.
Error Details:
missing authority of tester
我们可以通过赋予 tester
的权限可来解决这个问题:
$ cleos push action hello.code hi '["tester"]' -p tester
executed transaction: 235bd766c2097f4a698cfb948eb2e709532df8d18458b92c9c6aae74ed8e4518 244 bytes 1000 cycles
# hello.code <= hello.code::hi {"user":"tester"}
>> Hello, tester
部署交易合约
与上面的例子类似,我们可以部署交易合约。假设这是从 EOSIO 源的根运行的。
$ cleos create account eosio exchange EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
executed transaction: 4d38de16631a2dc698f1d433f7eb30982d855219e7c7314a888efbbba04e571c 364 bytes 1000 cycles
# eosio <= eosio::newaccount {"creator":"eosio","name":"exchange","owner":{"threshold":1,"keys":[{"key":"EOS7ijWCBmoXBi3CgtK7DJxe...
$ cleos set contract exchange build/contracts/exchange -p exchange
Reading WAST...
Assembling WASM...
Publishing contract...
executed transaction: 5a63b4de8a1da415590778f163c5ed26dc164c960185b20fd834c297cf7fa8f4 35172 bytes 10000 cycles
# eosio <= eosio::setcode {"account":"exchange","vmtype":0,"vmversion":0,"code":"0061736d0100000001f0023460067f7e7f7f7f7f00600...
# eosio <= eosio::setabi {"account":"exchange","abi":{"types":[{"new_type_name":"account_name","type":"name"}],"structs":[{"n...
中文译本,2018-04-09,如有问题请 Issue。