一、智能合约入门
本教程的目的是演示如何设置可用于实验智能合约的本地区块链。
本教程的第一部分将着重于讲述:
本教程的第二部分将引导您创建和部署自己的合约:
本教程假设您已经安装了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秒看到一次块生成消息。 这意味着您的本地区块链处于活动状态,能生成区块并已可以使用。有关 `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,你必须先解锁你的钱包,然后才能使用其中的密钥。
$ cleos wallet unlock --password PW5JuBXoXJ8JHiCTXfXcYuJabjF9f9UNNqHJjqDVY7igVffe3pXub
Unlocked: default
直接在命令行中使用密码,并将其记录到bash历史记录中是不安全的,因此您也可以在交互模式下解锁:
$ cleos wallet unlock
password:
出于安全考虑,最好在不使用钱包时锁定钱包。要锁定你的钱包而不关闭nodeos,你可以这样做:
$ cleos wallet lock
Locked: default
本教程的其余部分需要您解锁您的钱包。 译者注:原文没有讲私钥导入的过程。为了完成后续部分教程,您还需要在钱包解锁状态下导入eosio的私钥。eosio的私钥是公开的。
$ cleos wallet import –n default 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
四、加载bios合约
现在我们有一个钱包,并且加载了eosio帐户的密钥,我们可以设置一个默认的系统合约。为了开发的目的,可以使用默认的eosio.bios合约。通过此合约,您可以直接控制其他帐户的资源分配,并调用其他特权API。在公开区块链中,这个系统合约将管理其他账户的 token 抵押和解抵押操作,以为合约执行预留CPU、网络活动带宽,以及预留内存。
eosio.bios合约可以在你的EOSIO源代码文件夹中找到: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发起一个包含两个操作(actions)的交易(transaction):eosio::setcode和eosio::setabi。
代码定义了合约如何运行,abi描述了参数如何在二进制和json表示之间进行转换。虽然abi在技术上是可选的,但为了便于使用,所有的EOSIO工具都依赖于它(译者注:所以还是必须的。)。
任何时候你执行一个交易(transaction),都会看到如下输出:
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账户给予的{args...}参数来执行。(译者注:这句话有点绕。eos里边每个账户只能发布一个合约,所以它认为账户与合约是对应的,账户下可以没有合约,如果有则只能有一个。另外,这个合约发布也是用eosio账户的权限签名的。)
# ${executor} <= ${contract}:${action} ${args...}
> console output from this execution, if any
正如我们稍后会看到的,操作(actions)可以由多个合约处理。
这个命令最后一个参数是-p eosio。该参数告诉cleos,用eosio账户的active权限签署此操作,即使用我们先前导入钱包的eosio账户私钥对操作进行签名。
五、创建账户
现在我们已经建立了基本的系统合同,可以开始创建自己的账户。我们将创建两个帐户,user和tester,我们需要将密钥与每个帐户相关联。在这个例子中,两个帐户都使用相同的密钥。( 译者注:从这里可以看出,eos里边账户和密钥对之间并没有严格的对应关系,A账户完全可以和B账户使用相同的的密钥对 )
为此,我们首先为账户生成一个密钥。
$ 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"
]
}
六、Eosio.token,Exchange和Eosio.msig合约
我们来部署eosio.token合同,该合约允许在同一个合约上,创建并运行许多不同的token,但这些token可能由不同的用户管理。
在部署token合同之前,必须创建一个帐户来部署它。这里创建的账户名是eosio.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
您可以在contracts/eosio.token/eosio.token.hpp头文件中查看eosio.token合约提供的操作(actions):
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 );
要创建一个新的token,我们必须用适当的参数调用create(...)操作。该命令将使用最大供应量(maximum_supply)的token符号来唯一标识该token。发行人(issuer) 将有权调用发行(issue)操作,和执行其他操作,例如冻结(freezing),召回(recalling)以及将用户列入白名单(whitelisting)。
调用此操作的简洁方法如下(序列化参数):
$ 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...
或者,使用命名参数调用此操作的更为详细的的方法:
$ 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.token合约账户的授权,因为它"拥有"token符号命名空间(例如,“EOS”)。本合约未来的版本可能允许其他账户购买token符号名称。所以,我们必须通过-p eosio.token对此调用进行授权。
八、给账户“User”发行Token
现在我们已经创建了名为EOS的token,发行人可以向我们之前创建的user帐户发行新的token。
我们使用序列化参数方式调用操作(或者使用命名参数方式)。
$ 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)和三次转账(transfer)。虽然我们执行的唯一一项操作是issue,但该issue操作执行了“内联转账 (inline transfer)”,“内联转账”通知发送者和收款人帐户。输出内容展示了操作中所有被调用的处理程序被调用的顺序,以及其产生的所有输出。
从技术上讲,eosio.token合同可以跳过"内联转账"inline transfer步骤,并选择直接修改余额。然而,在eosio.token合约中遵循了token转账的惯例做法,即要求所有账户的余额都可以通过引用它们的转账行为的总和来推导。它还要求通知资金的发送者和收款人,以便他们能够自动处理存款和提款。
如果你想看到交易(transaction)被广播的实际情况,可以使用-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": []
}
九、给账户“Tester”转账
现在user账户有token了,我们会将一些token转账给账户tester。我们用授权参数-p user表示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"}
十、部署Exchange合同
与上述示例类似,我们可以部署exchange合约,该合约提供了创建和交易货币的功能。跟以前一样,我们假定下面的命令是从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...
十一、部署Eosio.msig合同
eosio.msig合约允许多方异步签署单个交易。作为一种基础功能,EOSIO提供了多重签名(multisig)支持,但它需要一个同步侧通道,数据在这个通道中传输并签名。 Eosio.msig是一个对用户更加友好的方式,异步提出、批准并最终发布多方同意的交易。
以下步骤可用于部署eosio.msig合同。
$ cleos create account eosio eosio.msig EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
# eosio <= eosio::newaccount {"creator":"eosio","name":"eosio.msig","owner":{"threshold":1,"keys":[{"key":"EOS7ijWCBmoXBi3CgtK7DJ...
$ cleos set contract eosio.msig build/contracts/eosio.msig -p eosio.msig
Reading WAST...
Assembling WASM...
Publishing contract...
executed transaction: a113a7db8c878dfd894671792770b59a04efb3aa8295f5b3d585daf89c314ec9 8964 bytes 10000 cycles
# eosio <= eosio::setcode {"account":"eosio.msig","vmtype":0,"vmversion":0,"code":"0061736d0100000001bd011b60047f7e7e7f0060047...
# eosio <= eosio::setabi {"account":"eosio.msig","abi":{"types":[{"new_type_name":"account_name","type":"name"},{"new_type_na...