以太坊智能合约定义的全局变量的值是持久性的,就相当于智能合约一直在运行着。而EOS的智能合约更加接近我们平时使用的程序,每次执行action都相当于启动智能合约的一个新实例,一旦执行完,代码定义的变量就释放了,不会影响下一次执行环境。但是智能合约肯定需要有持久化存储的需求,比如永久保存智能合约代币的状态,不能代币转账执行完,代币的balance余额信息和转账前一样吧。这个持久化存储就是数据库存储数据。EOS允许智能合约定义自己的私有数据库表。比如下图,Apply Context的内容都是一次性的,一次action执行完成,对象就释放了,只有存储到EOSIO database的才被保存。

这个私有数据表是通过multi\_index来访问和交互的。EOS的multi\_index类似boost的multi\_index,即多索引容器。有了多级索引,智能合约就具备了操作类似数据库模块的功能。eosio::multi\_index支持主键,但是必须是唯一的无符号64位整数。eosio::multi\_index中的对象容器按主键索引按无符号64位整数主键的升序排序。

申请私有数据表

定义一个multi_index对象即可

纯数据表

multi_index<table_name, record> table( code, scope );

这里的code必须是contract的account_name, 因为这个数据表的操作api都有check,比如'modify' 函数.

void modify( const T& obj, uint64_t payer, Lambda&& updater ) {
      eosio_assert( _code == current_receiver(), "cannot modify objects in table of another contract" ); // Quick fix for mutating db using multi_index that shouldn't allow mutation. Real fix can come in RC2.
}

scope参数,这个参数的初衷是用来实现交易action并行执行的。我们知道,action之间可能是存在依赖的,比如alice转给bob 5块钱(action1),alice转给james 5块钱(action2), action1和action2就有依赖关系。因为如果Alice只有6块钱,那么这两个action中必然有一个action是失败的。如果这两个action并行执行那这两个都会成功,明显是不合理的。你可能会说可以使用互斥锁解决冲突啊?事实上市不行的,申请互斥锁中的两段代码的执行顺序是随机的,所以导致action1和action2的执行顺序是随机的,这就违背了区块链的基本要求---确定性和可重复性,每个节点按相同顺序执行一个action列表,结果应该是一致的。所以为了解决这个问题就提出了scope的。假设这个balance的数据保存在‘balance’数据表里,scope为'balance_scope', 所以在提交action1时就会传入一个scope参数'balance_scope‘,这样系统根据action1和action的scope很容易知道action1, action2都会操作'balance_scope'对应的'balance'表,就不会让这两个action并行执行。其实,这里的核心问题是action操作的数据表的信息,如果提交的action不告知系统,系统必须要执行完action才能知道操作了哪个数据表,这自然没法实现并行。当然,目前版本scope基本没有起作用,action并行老版本有实现过的框架(但不起作用),最新的版本框架甚至已经删除了相关逻辑,看来action的并行化路漫慢而修远兮。

纯数据表示例

eosio.token合约

structcurrency_stats {

asset supply;

asset max_supply;

ccount_name issuer;

//必须有该函数

uint64_t primary_key()const { return supply.symbol.name(); }

};

typedefeosio::multi_index<N(stat), currency_stats>stats;

主键查找数据

纯数据表只能通过table.find函数以主键为参数查找数据

stats statstable( _self, sym.name() );
autoexisting=statstable.find( sym.name() );

多索引数据表

基本格式

multi_index<table_name, record,
 indexed_by<index_name, index_func>,
 indexed_by<index_name_1, index_func_1>,
 …>
> table( code, scope );

和纯数据表相比,就多了index_by索引定义

索引的定义格式是:indexed_by<索引名,索引键值函数>,比如这个实例:

eosio::multi_index<N(orders), limit_order,
 indexed_by< N(byexp), const_mem_fun<limit_order, uint64_t, &limit_order::get_expiration>>,
 indexed_by< N(byprice), const_mem_fun<limit_order, uint128_t, &limit_order::get_price>>
> orders( N(multitest), N(multitest) );

'<N(orders), limit_order,'这部分和纯数据表是一样的,第一个参数是表名,第二个参数是表行对象。这里有两个索引对象。分别是

indexed_by< N(byexp), const_mem_fun<limit_order, uint64_t, &limit_order::get_expiration>>
indexed_by< N(byprice), const_mem_fun<limit_order, uint128_t, &limit_order::get_price>>
  • N(byprice):索引名称是'byprice'

  • const_mem_fun <limit_order, uint128_t,&limit_order::get_price>

const_mem_fun这部分定义了一个索引键值函数,这个其实是通过boost::bind函数将对象的const成员函数转化为函数指针.其格式是<ObjectType, indexType(索引类型), &ObjectType::function>

索引查找数据

auto priceidx = orders.get_index<N(byprice)>();

print("Items sorted by price:\n");

for( const auto& item : priceidx) {

print(" ID=", item.id, ", price=", item.price, ", owner=", name{item.owner}, "\n");

}

二级索引类型限制

  • idx64 - 原始的64位无符号整数密钥.
  • idx128 - 原始128位无符号整数密钥.
  • idx256 - 256位固定大小的字典键.
  • idx_double - 双精度浮点键.
  • idx_long_double - 四倍精度浮点键.

查看数据表数据

$cleos get tablecleos get table [OPTIONS] contract scope table

实例运行

源码

class tabletest : public eosio::contract {
private:
/// @abi table
struct contact {
account_name name;
uint64_tphone;

auto primary_key()const { return name; }
uint64_t get_phone()const { return phone; }

EOSLIB_SERIALIZE( contact, (name)(phone))
};
typedef eosio::multi_index<N(contact), contact, indexed_by< N(byphone), const_mem_fun<contact, uint64_t, &contact::get_phone>>
> contacts;
public:
using contract::contract;

/// @abi action
void add(account_name owner, account_name name, uint64_t phone ) {
//新增数据项
contacts contacttable( _self, owner );//(code, scope)
//(player, item),player域指由谁来支付这个数据存储费用
contacttable.emplace( _self, [&]( auto& o ) {
o.name = name;
o.phone = phone;
});
}

/// @abi action
void remove(account_name owner, account_name contact_name) {
//删除数据项
contacts contacttable( _self, owner );
name n{contact_name};
auto item = contacttable.find( contact_name );
if( item == contacttable.end() ) {
print_f("Not found name:%sn", n.to_string().data());
} else {
contacttable.erase(item);
};
}

/// @abi action
void modify(account_name owner, account_name contact_name, uint64_t phone) {
//修改数据项
contacts contacttable( _self, owner );
name n{contact_name};
auto item = contacttable.find( contact_name );
if( item == contacttable.end() ) {
print_f("Not found name:%sn", n.to_string().data());
} else {
contacttable.modify( item, _self, [&]( auto& o ) {
o.phone = phone;
});
};
}

/// @abi action
void findbyname(account_name owner, account_name contact_name) {
//根据name主键查找数据项
contacts contacttable( _self, owner );
name n{contact_name};
auto item = contacttable.find( contact_name );
if( item == contacttable.end() ) {
print_f("Not found name:%s\n", n.to_string().data());
} else {
n = name{item->name};
print_f("Found phone:%, for %s\n", item->phone, n.to_string().data());
};
}


/// @abi action
void findbyphone(account_name owner, uint64_t phone) {
 //根据phone索引查找数据项
contacts contacttable( _self, owner );
auto phoneinx = contacttable.get_index<N(byphone)>();
auto item = phoneinx.find(phone);
if( item == phoneinx.end() ) {
print_f("Not found phone:%\n", phone);
} else {
name n{item->name};
print_f("Found name:%s for phone:%\n", n.to_string().data(), item->phone);
};
}
};

EOSIO_ABI( tabletest, (add) (remove) (modify) (findbyname) (findbyphone))

编译

请参考我的博文【EOS编写HelloWorld智能合约

执行

$cleos create account eosio table.code $KEY_PUB_1 $KEY_PUB_1
$cleos set contract table.code ./table -p table.code
$cleos create account eosio itleaks $KEY_PUB_2 $KEY_PUB_2
$cleos push action table.code add '[ "itleaks", "jackma", "13456" ]' -p itleaks
$cleos get table table.code itleaks contact

源码一键实践

从以下github网页下载源码,即可一键实践执行该智能合约

https://github.com/itleaks/eos-contract/tree/master/table-exp/

/********************************

* 本文来自CSDN博主"爱踢门"

* 转载请标明出处:http://blog.csdn.net/itleaks

******************************************/

EOS技术交流群,EOS开发群,以太坊技术群:787804520

公众号:

results matching ""

    No results matching ""