在日常的 EOS 合约开发中,我们一般使用 multi_index 作为表来保存数据,这是一个多索引链表结构。在需要存储某个结构 ( struct )的多个实例的情况下是非常有用的。
但如果只有一个实例,比如配置,比如总的状态,那么使用 multi_index 则显得有点复杂了。
为了应对这种需求,官方推出了 eosio::singleton 单例表,这个表在文件 <eosiolib/singleton.hpp> 中定义。
如果你看了源代码,就会发现它只是对 multi_index 的一个简单的封装。
但是,这个 eosio::singleton 在生成的 ABI 是错误的。比如下面的代码
hh.cpp
#include <eosiolib/eosio.hpp> #include <eosiolib/singleton.hpp> using namespace eosio; class [[eosio::contract]] hh : public contract { public: using contract::contract; [[eosio::action]] void hi() { print("Hello World"); } struct [[eosio::table]] tbl_config{ uint64_t bonus_down; uint64_t bonus_up; }; typedef eosio::singleton<"config"_n, tbl_config> config; }; EOSIO_DISPATCH( hh, (hi))
编译后的 ABI 内容如下
{
"____comment": "This file was generated with eosio-abigen. DO NOT EDIT Sun Nov 18 12:25:49 2018",
"version": "eosio::abi/1.1",
"structs": [
{
"name": "hi",
"base": "",
"fields": []
},
{
"name": "tbl_config",
"base": "",
"fields": [
{
"name": "bonus_down",
"type": "uint64"
},
{
"name": "bonus_up",
"type": "uint64"
}
]
}
],
"types": [],
"actions": [
{
"name": "hi",
"type": "hi",
"ricardian_contract": ""
}
],
"tables": [
{
"name": "tbl_config",
"type": "tbl_config",
"index_type": "i64",
"key_names": [],
"key_types": []
}
],
"ricardian_clauses": [],
"variants": [],
"abi_extensions": []
}
粗看没什么错误。
但是,如果你使用过 multi_index 的版本,就会发现一个问题,"tables": 里的 name 是错误,正常应该是 config 才对
这种情况下,如果我们部署合约,就会报错
Reading WASM from ../hh/hh.wasm... Skipping set code because the new code is the same as the existing code Error 3010001: Invalid name Name should be less than 13 characters and only contains the following symbol .12345abcdefghijklmnopqrstuvwxyz Error Details: Name not properly normalized (name: tbl_config, normalized: tbl.config)
这个
Error 3010001: Invalid name Name should be less than 13 characters and only contains the following symbol .12345abcdefghijklmnopqrstuvwxyz
的错误,原因就是刚刚说的那个 "tables": 里的 name 导致的。
面对这个问题,目前只有一个解决方案,就是在添加一个多余的 multi_index 定义
typedef eosio::multi_index<"config"_n, tbl_config> dump_for_config;
完整的代码如下
#include <eosiolib/eosio.hpp> #include <eosiolib/singleton.hpp> using namespace eosio; class [[eosio::contract]] hh : public contract { public: using contract::contract; [[eosio::action]] void hi() { print("Hello World"); } struct [[eosio::table]] tbl_config{ uint64_t bonus_down; uint64_t bonus_up; }; typedef eosio::singleton<"config"_n, tbl_config> config; typedef eosio::multi_index<"config"_n, tbl_config> dump_for_config; }; EOSIO_DISPATCH( hh, (hi))
这样,生成的 ABI 文件才是正常的
{
"____comment": "This file was generated with eosio-abigen. DO NOT EDIT Sun Nov 18 12:27:26 2018",
"version": "eosio::abi/1.1",
"structs": [
{
"name": "hi",
"base": "",
"fields": []
},
{
"name": "tbl_config",
"base": "",
"fields": [
{
"name": "bonus_down",
"type": "uint64"
},
{
"name": "bonus_up",
"type": "uint64"
}
]
}
],
"types": [],
"actions": [
{
"name": "hi",
"type": "hi",
"ricardian_contract": ""
}
],
"tables": [
{
"name": "config",
"type": "tbl_config",
"index_type": "i64",
"key_names": [],
"key_types": []
}
],
"ricardian_clauses": [],
"variants": [],
"abi_extensions": []
}
部署时才不会失败
cleos set contract hh ../hh -p hh
Reading WASM from ../hh/hh.wasm...
Publishing contract...
executed transaction: 9d41655faff0df76948d4f2ed26d562834847858bc161abaa2b3dfdafdfeab98 1432 bytes 7580 us
# eosio <= eosio::setcode {"account":"hh","vmtype":0,"vmversion":0,"code":"0061736d0100000001300960017f006000017f60027f7f017f6...
# eosio <= eosio::setabi {"account":"hh","abi":"0e656f73696f3a3a6162692f312e31000202686900000a74626c5f636f6e66696700020a626f6...