距离EOS主网上线还有不到一周,现在持有EOS的人请注意:如果不在交易所的,一定要去注册下。近日,区块链领域专家谷老师做了关于EOS部署、智能合约应用开发和代币映射内容的技术分享,引起了众多同行的关注。
目前最大的EOS应用是区块链百科(https://everipedia.org/),已获得7500万美元的融资,它对标的是维基百科。这个应用的大概规则是:比如当你要编辑一个词条时,你需要抵押自己的Token,然后每天会产生限量token激励,社区里的事情都要进行投票,进行自治,大于75%的投票,才可以修改词条。这是因为它的内容是存放在IPFS里。当然,还有很多EOS应用,我只挑了一个典型的。
EOS如何部署私有环境?
https://github.com/EOSIO/eos/wiki
编译命令:https://github.com/EOSIO/eos/wiki/Local-Environment#2-building-eosio
EOS网络是由无数个nodes组成的,提供单独keosd 钱包节点,可以单独下。相对而言,以太坊节点会自动同步数据,会很慢。
启动EOS节点命令:./nodeos -e -p eosio --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin
使用cleos创建钱包及账号(https://github.com/EOSIO/eos/wiki/Programs-&-Tools#cleos),这里要注意EOS钱包和以太坊钱包的区别:EOS主要管理私钥,不是一个离线账号,是建立在EOS节点上注册后的账号。
cleos -u http://47.98.56.32:8888/ --wallet-url http://47.98.56.32:9999/ wallet unlock
cleos -u http://47.98.56.32:8888/ --wallet-url http://47.98.56.32:9999/ wallet list
管理2组账号,第一组账号是eosio,其他所有eos账号都是在第一个eosio上派生出来的。
cleos -u http://47.98.56.32:8888/ --wallet-url http://47.98.56.32:9999/ create key
我们先来创建私钥:
cleos -u http://47.98.56.32:8888/ --wallet-url http://47.98.56.32:9999/ wallet import 私钥
现在就多了一组账号。
然后开始创建EOS上的账户。
以太坊的账号是一个40字符的字符串,很难被记住。EOS权限分组,owner是最高权限,active 可以管理账号的资产。
cleos -u http://47.98.56.32:8888/ --wallet-url http://47.98.56.32:9999/ create account eosio 唯一名字(可以抢注) OwnerKey ActiveKey
这样你的唯一名字就注册到EOS网络上了,别人可以直接给你这个名字的账号里打钱或者各种操作,以上操作意味着创建账户和管理私钥的内容就完成了。
接下来就是创建合约,先来讲2个案例:
第一个案例:飞行宝
飞行宝的具体逻辑是:用户要坐飞机,在飞行前,用户买了哪一期的哪一个航班,EOS提供了一个多索引的数据表结构叫multi_Index,
uint64_t term_Id; 每一天开一期
account_name 就是我们刚刚在eos上注册的唯一用户名,代表购买者账号
uint64_t content; 可以留言
account_name get_poster() const { return account }
fmulti_index多索引,类似一个mysql一个表名,第一个字段是表名,后面是列,就是定义了一个数据表。
总共有4个操作:第一个操作是购买某一天的航班,购买就是一个添加数据的过程;第二操作是获取购买的情况;第三个操作是查询你的所有购买记录,返回一个数组;第四个操作是修改操作。
我们来实际操作下:
eosiocpp -o flybaby.wast flybaby.cpp
生成一个 webassembly 文件,这是ABI文件(现在EOS网络还有一些bug,要手动改下某几处ABI文件才可以运行)。
现在开始部署合约
cleos -u http://47.98.56.32:8888/ --wallet-url http://47.98.56.32:9999/ set contract 唯一名字 flybaby
和以太坊有区别的是EOS可以修改智能合约。
接下来是调用方法
cleos -u http://47.98.56.32:8888/ --wallet-url http://47.98.56.32:9999/ push action 唯一名字 create '[1, 1, "唯一名字", 3000, "备注"]' -p 唯一名字
说明调用成功
cleos -u http://47.98.56.32:8888/ --wallet-url http://47.98.56.32:9999/ get table 唯一名字 唯一名字 fdata
返回一个JSON数据,用一个新账号再来买一个。返回2个飞行宝购买记录,说明成功。get 方法是获取某一天的航班,list方法返回我所有的飞行宝购买记录。
合约源码:
flybaby.cpp
#include
#include
using namespace eosio;
using std::string;
class flybaby : public eosio::contract {
public:
//self 代表合约拥有者账号.
flybaby( account_name self ):contract(self){}
/// @abi table
struct record {
uint64_t term_id; //飞行宝期数+航班ID作为主键
account_name customer; //购买者账号
uint64_t amount; //购买数量
string content; //留言
//客户在当期仅能买一次某航班,但可以买多个不同航班.
uint64_t primary_key()const { return term_id; }
//根据客户筛选出他购买的航班列表.
account_name get_poster() const { return customer; }
EOSLIB_SERIALIZE(record, (term_id)(customer)(amount)(content))
};
typedef eosio::multi_index
indexed_by
const_mem_fun
using contract::contract;
/// @abi action
void create(uint64_t term,uint64_t id,account_name user,uint64_t amount, string content) {
require_auth( user ); //验证权限,只能用自己的账号给你自己买.
records datable( _self, _self); //定义数据库对象,数据库属于合约创建者,并且都存在一个表中.
//简化仅表达意思,没做校验,注意运算符优先级.
uint64_t term_id = (term << 32) + id;
datable.emplace(user, [&]( record & d){
eosio::print("ok this is lamda");
//d.term_id = datable.available_primary_key();
d.term_id = term_id;
d.customer = user;
d.amount = amount;
d.content = content;
eosio::print("update");
});//数据库内容创建
}
void get(uint64_t term,uint64_t id,account_name user) {
require_auth(user);
records datable(_self, _self);
uint64_t term_id = (term << 32) + id;
auto info = datable.find(term_id);
eosio::print("Term_id: ", info->term_id,
" Customer: ", name{info->customer},
" Amount: ", info->amount,
" Content: ", info->content.c_str());
}
void list(account_name user) {
require_auth(user);
records datable(_self, _self);
auto poster_index = datable.template get_index
auto pos = poster_index.find( user );
for (; pos != poster_index.end(); pos++)
{
eosio::print("Term_id: ", pos->term_id,
" Customer: ", name{pos->customer},
" Amount: ", pos->amount,
" Content: ", pos->content.c_str());
eosio::print("||");
}
}
void change(account_name user, uint64_t term, uint64_t id, string content)
{
require_auth(user);
records datable( _self, _self);
uint64_t term_id = (term << 32) + id;
auto info = datable.find(term_id);
eosio_assert(info->customer == user, "not your account");
//此处payer不是user
datable.modify(info, _self, [&](auto& p){
if (content != "")
p.content = content;
});
}
void dele(account_name user, uint64_t term, uint64_t id)
{
require_auth(user);
records datable( _self, _self);
uint64_t term_id = (term << 32) + id;
auto info = datable.find(term_id);
eosio::print(info->content.c_str());
eosio_assert(info->customer == user, "not your account");
datable.erase(info);
}
};
EOSIO_ABI(flybaby, (create)(get)(list)(change)(dele))
接下来讲解 Token 的合约
EOS上没有ERC20的协议,来看下逻辑:
currency_stats代表一个资产的结构体,有资产的代号、名称;
max_supply 最大发行量;
issuer 是代币的发行者;
填写好Token的名字、代号、发行量,做好准备工作。
require_auth(st.issuer) 只有发行者可以修改当前的发行量;
token::issue 方法就是发布出去;
token::transfer 就是转账功能;
就是对余额进行增加、减少的处理。
合约源码:
token.cpp
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include "mydogcon.hpp"
namespace eosio {
void token::create( account_name issuer,
asset maximum_supply )
{
require_auth( _self );
auto sym = maximum_supply.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" );
eosio_assert( maximum_supply.is_valid(), "invalid supply");
eosio_assert( maximum_supply.amount > 0, "max-supply must be positive");
stats statstable( _self, sym.name() );
auto existing = statstable.find( sym.name() );
eosio_assert( existing == statstable.end(), "token with symbol already exists" );
statstable.emplace( _self, [&]( auto& s ) {
s.supply.symbol = maximum_supply.symbol;
s.max_supply = maximum_supply;
s.issuer = issuer;
});
}
void token::issue( account_name to, asset quantity, string memo )
{
auto sym = quantity.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" );
auto sym_name = sym.name();
stats statstable( _self, sym_name );
auto existing = statstable.find( sym_name );
eosio_assert( existing != statstable.end(), "token with symbol does not exist, create token before issue" );
const auto& st = *existing;
require_auth( st.issuer );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must issue positive quantity" );
eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
eosio_assert( quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply");
statstable.modify( st, 0, [&]( auto& s ) {
s.supply += quantity;
});
add_balance( st.issuer, quantity, st, st.issuer );
if( to != st.issuer ) {
SEND_INLINE_ACTION( *this, transfer, {st.issuer,N(active)}, {st.issuer, to, quantity, memo} );
}
}
void token::transfer( account_name from,
account_name to,
asset quantity,
string /*memo*/ )
{
eosio_assert( from != to, "cannot transfer to self" );
require_auth( from );
eosio_assert( is_account( to ), "to account does not exist");
auto sym = quantity.symbol.name();
stats statstable( _self, sym );
const auto& st = statstable.get( sym );
require_recipient( from );
require_recipient( to );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must transfer positive quantity" );
eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
sub_balance( from, quantity, st );
add_balance( to, quantity, st, from );
}
void token::sub_balance( account_name owner, asset value, const currency_stats& st ) {
accounts from_acnts( _self, owner );
const auto& from = from_acnts.get( value.symbol.name() );
eosio_assert( from.balance.amount >= value.amount, "overdrawn balance" );
if( from.balance.amount == value.amount ) {
from_acnts.erase( from );
} else {
from_acnts.modify( from, owner, [&]( auto& a ) {
a.balance -= value;
});
}
}
void token::add_balance( account_name owner, asset value, const currency_stats& st, account_name ram_payer )
{
accounts to_acnts( _self, owner );
auto to = to_acnts.find( value.symbol.name() );
if( to == to_acnts.end() ) {
to_acnts.emplace( ram_payer, [&]( auto& a ){
a.balance = value;
});
} else {
to_acnts.modify( to, 0, [&]( auto& a ) {
a.balance += value;
});
}
}
} /// namespace eosio
EOSIO_ABI( eosio::token, (create)(issue)(transfer) )
token.hpp
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include
#include
#include
namespace eosiosystem {
class system_contract;
}
namespace eosio {
using std::string;
class token : public contract {
public:
token( account_name self ):contract(self){}
void create( account_name issuer,
asset maximum_supply);
void issue( account_name to, asset quantity, string memo );
void transfer( account_name from,
account_name to,
asset quantity,
string memo );
private:
friend eosiosystem::system_contract;
inline asset get_supply( symbol_name sym )const;
inline asset get_balance( account_name owner, symbol_name sym )const;
private:
struct account {
asset balance;
uint64_t primary_key()const { return balance.symbol.name(); }
};
struct currency_stats {
asset supply;
asset max_supply;
account_name issuer;
uint64_t primary_key()const { return supply.symbol.name(); }
};
typedef eosio::multi_index
typedef eosio::multi_index
void sub_balance( account_name owner, asset value, const currency_stats& st );
void add_balance( account_name owner, asset value, const currency_stats& st,
account_name ram_payer );
public:
struct transfer_args {
account_name from;
account_name to;
asset quantity;
string memo;
};
};
asset token::get_supply( symbol_name sym )const
{
stats statstable( _self, sym );
const auto& st = statstable.get( sym );
return st.supply;
}
asset token::get_balance( account_name owner, symbol_name sym )const
{
accounts accountstable( _self, owner );
const auto& ac = accountstable.get( sym );
return ac.balance;
}
} /// namespace eosio
提问环节
1:讲讲你的EOS信仰
谷老师:我一般对EOS失去信心的时候,我就会去reddit看下EOS上朋友们的留言,上面的消息是很及时,而且上面的朋友特别友好,给你不割肉的动力。比如:big news is coming soon……winter is coming……Image you are one of them……The Dawn is coming……
每当我难受的时候,我就来这里,我的信仰就是来自这里。
2:主链上线时间有没有风险?
谷老师:现在EOS 1.0 上线已经完成 94%,EOS还是可以的,有一定风险会延迟。现在讲一下EOS百万TPS的梗:它一共21个超级节点,有点类似我们在用负载均衡,比如10台机器,轮训来分发流量,如果当前有1万交易,会分到某个节点上,由于单台服务器节点很高,能一次性处理。
另外我推荐ONO这个区块链应用,未来随着区块链的升级换代,提升基础设施,也许我们后端开发就不像现在这样去买一台云服务器,而未来所有的后端就是一个区块链,所有应用都会直接构建到区块链上,像聊天、打车就直接构建在像这样的EOS区块链上,打车的结算公开在区块链上,就不存在杀熟的问题。甚至公司的形式也会发生变化,而是由使用者来决定。未来区块链就不只是炒币的功能。
3:如果一个用户开发了一个很好的应用,大户如果抄袭,大户会不会抢走EOS运行的资源?
谷老师:根据你抵押的EOS份额来租用算力。这是一个商业逻辑,如果你的应用很火,就会有资本投你,算力不太可能被垄断。
谷老师在最后还讲到,伴随夏季的来临,航班延误或取消都进入了高发期。而航空延误险则是很多常旅客的必备,而全球区块链互助社区HMS,其基于区块链技术和底层的风险保障逻辑,设计出的全自动的智能型全球航空飞行延误互助保障合约——“飞行宝”现在已完成首例赔付。
一直以来,区块链技术和基于区块链的项目,因为与现实世界脱节而蒙上了神秘的面纱。与此同时,底层技术不够成熟、缺乏智能合约公链平台,影响了区块链技术的发展以及应用化的过程。HMS一直在探索区块链技术和互助业务相结合的创新,为更多的现实场景提供服务,对促进区块链世界与现实世界的进一步联动有深刻意义,他非常看好这一应用。