在日常的 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...