- 区块链
【免费下载链接】eos
An open source smart contract platform
cleos get scope是 eos 区块链命令行工具集(programs/cleos)中用于**枚举某个智能合约账户所拥有的全部表作用域(scope)与表(table)**的只读查询命令。它回答了一个在开发与运维中非常常见的问题:eosio.token这类合约到底在哪些账户作用域下维护了哪些表、每个表有多少行数据、RAM 由谁支付。读完本文,你将完整掌握该命令的位置参数与全部选项的语义、输出字段含义、基于more字段的分页技巧,以及从 CLI 到 RPC 再到链插件底层的完整调用链实现原理。
命令概览:作用域与表的关系
在 eos 的存储模型中,多索引表(multi-index table)由三元组唯一标识:code(合约账户)→ scope(作用域)→ table(表名)。其中:
code是部署合约的账户,即表的所有者;scope是数据的隔离分区,通常是一个账户名,例如eosio.token合约会为每个持有代币的账户各自维护一份accounts表;table是表名,与合约 ABI 中声明的表结构对应。
cleos get table(见 docs/02_cleos/03_command-reference/get/table.md)用于读取指定account + scope + table下的具体数据行;而cleos get scope则向上一个层级,列出某个合约名下所有已存在的 scope/table 组合,相当于先"侦察"合约的存储布局,再决定去读取哪张表。两者搭配使用是排查合约数据的最常用组合拳。
位置参数
| 参数 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
contract | TEXT | 是 | 拥有这些表的合约账户名,即表的所有者(对应 RPC 参数code) |
在 programs/cleos/main.cpp 中,该参数通过getScope->add_option( "contract", code, ... )->required()注册为必填:
auto getScope = get->add_subcommand( "scope", localized("Retrieve a list of scopes and tables owned by a contract")); getScope->add_option( "contract", code, localized("The contract who owns the table") )->required();选项详解
| 选项 | 类型 | 说明 |
|---|---|---|
-t, --table | TEXT | 表名过滤条件,仅返回指定名称的表,可选 |
-l, --limit | UINT | 返回的最大行数,可选;服务端默认值为 10 |
-L, --lower | TEXT | scope 的下界(lower bound),可选,用于范围查询与翻页 |
-U, --upper | TEXT | scope 的上界(upper bound),可选 |
-r, --reverse | 标志 | 以逆序迭代返回结果,可选 |
这些选项在 programs/cleos/main.cpp 中逐一注册,回调中将它们打包为 JSON 请求体:
getScope->add_option( "-t,--table", table, localized("The name of the table as filter") ); getScope->add_option( "-l,--limit", limit, localized("The maximum number of rows to return") ); getScope->add_option( "-L,--lower", lower, localized("Lower bound of scope") ); getScope->add_option( "-U,--upper", upper, localized("Upper bound of scope") ); getScope->add_flag("-r,--reverse", reverse, localized("Iterate in reverse order")); getScope->callback([&] { auto result = call(get_table_by_scope_func, fc::mutable_variant_object("code",code) ("table",table) ("lower_bound",lower) ("upper_bound",upper) ("limit",limit) ("reverse", reverse) ); std::cout << fc::json::to_pretty_string(result) << std::endl; });其中get_table_by_scope_func在 programs/cleos/httpc.hpp 中定义为:
const string get_table_by_scope_func = chain_func_base + "/get_table_by_scope";即该命令最终发送的 HTTP 请求为POST /v1/chain/get_table_by_scope。
选项语义的补充说明
--limit默认值与-l缺省:CLI 层不强制要求--limit,服务端结构体默认limit = 10(见 plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp),因此不带-l时默认最多返回 10 行。--lower/--upper作用于 scope:这两个边界作用于 scope 字段(按 uint64 数值排序),并非作用于表名。当--lower或--upper传入的是普通账户名时,会被内部解析为 uint64 数值(name的本质就是一个 64 位整数)参与范围比较。--table是精确过滤而非模糊匹配:它按表名做精确筛选,不传则返回该合约下所有 scope/table 组合。
输出格式:rows、more 与分页
服务端返回的 JSON 结构在 plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp 中定义:
struct get_table_by_scope_result_row { name code; // 合约账户 name scope; // 作用域 name table; // 表名 name payer; // 该表项的 RAM 支付者 uint32_t count; // 该 scope+table 下的数据行数 }; struct get_table_by_scope_result { vector<get_table_by_scope_result_row> rows; string more; ///< fill lower_bound with this value to fetch more rows };对应的序列化反射为:
FC_REFLECT( eosio::chain_apis::read_only::get_table_by_scope_result_row, (code)(scope)(table)(payer)(count)); FC_REFLECT( eosio::chain_apis::read_only::get_table_by_scope_result, (rows)(more) );输出示例(格式示意)
假设查询eosio.token合约下的所有表:
cleos get scope eosio.token返回结果形如:
{ "rows": [{ "code": "eosio.token", "scope": "eosio", "table": "accounts", "payer": "eosio", "count": 1 },{ "code": "eosio.token", "scope": "alice", "table": "accounts", "payer": "alice", "count": 3 },{ "code": "eosio.token", "scope": "bob", "table": "accounts", "payer": "bob", "count": 2 } ], "more": "carol" }字段含义:
rows:命中的 scope/table 组合列表。每一行的code恒等于查询的合约名,scope是作用域(通常是账户名),table是表名,payer是该表项的 RAM 支付者账户,count是该表在该作用域下的行数。more:分页游标。当返回行数达到limit且有更多数据时,more会被设置为下一段的起始 scope 值;此时只需把more的值作为下一次请求的--lower,即可继续拉取后续数据。当没有更多数据时more为空字符串。
分页示例
# 第一页(默认 limit=10) cleos get scope eosio.token # 下一页:把上一页返回的 more 值作为 lower 继续查询 cleos get scope eosio.token -L carol这种"用more填充lower_bound"的游标翻页方式由结构体注释///< fill lower_bound with this value to fetch more rows明确约定,与cleos get table的分页机制保持一致。
源码级原理:从 RPC 入口到链插件实现
RPC 入口注册
/v1/chain/get_table_by_scope作为只读接口注册在 plugins/chain_api_plugin/chain_api_plugin.cpp:
CHAIN_RO_CALL(get_table_by_scope, 200, http_params_types::params_required),CHAIN_RO_CALL表明这是一个不修改链状态、不产生交易的只读查询,返回 HTTP 200 并强制要求请求体包含参数。
服务端实现要点
核心实现在 plugins/chain_plugin/chain_plugin.cpp 的read_only::get_table_by_scope中,其关键逻辑如下:
- 构造查询区间:以
(code, scope, table)三元组构建下界与上界查找元组,默认区间覆盖该合约下所有 scope/table;若提供了lower_bound/upper_bound,则将其解析为uint64_t并替换 scope 分量:
auto lower_bound_lookup_tuple = std::make_tuple( p.code, name(std::numeric_limits<uint64_t>::lowest()), p.table ); auto upper_bound_lookup_tuple = std::make_tuple( p.code, name(std::numeric_limits<uint64_t>::max()), (p.table.empty() ? name(std::numeric_limits<uint64_t>::max()) : p.table) ); if( p.lower_bound.size() ) { uint64_t scope = convert_to_type<uint64_t>(p.lower_bound, "lower_bound scope"); std::get<1>(lower_bound_lookup_tuple) = name(scope); }- 区间有效性检查:若上界小于下界,直接返回空结果(此时
more为空):
if( upper_bound_lookup_tuple < lower_bound_lookup_tuple ) return result;- 遍历表索引:通过
table_id_multi_index的by_code_scope_table索引定位区间,正向或逆向遍历(reverse时使用boost::make_reverse_iterator):
const auto& idx = d.get_index<chain::table_id_multi_index, chain::by_code_scope_table>(); auto lower = idx.lower_bound( lower_bound_lookup_tuple ); auto upper = idx.upper_bound( upper_bound_lookup_tuple ); if( reverse ) { walk_table_range( boost::make_reverse_iterator(upper), boost::make_reverse_iterator(lower) ); } else { walk_table_range( lower, upper ); }行数上限与游标:
table_receiver内部通过check_limit()在rows.size() >= limit时置位reached_limit_并停止遍历;当迭代器未走到区间末尾时,将itr->scope.to_string()写入more作为翻页游标(见 chain_plugin.cpp 与walk_table_range中的result.more = itr->scope.to_string();)。表名过滤:
table_receiver::add_table_row中if( params_.table && row.table != params_.table ) return;实现--table的精确过滤。
关于 backing store 的限制(重要前提)
该查询的 chainbase 分支实现完整;但从源码可见,当节点配置的 backing store 非 chainbase(如 RocksDB)时,get_table_by_scope会抛出"Support for configured backing_store has not been added to get_table_by_scope"(见 chain_plugin.cpp 与 chain_plugin.cpp)。因此,在使用非默认 backing store 的节点上执行该命令可能失败,这是由当前仓库实现决定的使用前提。
常见用法与最佳实践
1. 查看合约存储布局
查询一个合约(例如系统合约或代币合约)到底占用了哪些作用域与表:
cleos get scope eosio.token cleos get scope eosio.system2. 结合cleos get table深入读取
先用get scope定位目标作用域与表名,再用get table读取实际数据:
# 1) 找出 eosio.token 的所有 scope cleos get scope eosio.token # 2) 读取某个 scope 下的 accounts 表 cleos get table eosio.token eosio accounts3. 精确过滤与范围查询
只想看某个合约下名为accounts的表,或限定 scope 在指定账户名区间:
# 按表名过滤 cleos get scope eosio.token -t accounts # 限定 scope 范围(从 alice 开始,到 bob 结束) cleos get scope eosio.token -L alice -U bob # 逆序遍历 cleos get scope eosio.token -r4. 大数据量翻页
当合约作用域数量超过limit时,务必检查返回的more字段并携带-L继续拉取,避免遗漏:
cleos get scope eosio.token -l 100 cleos get scope eosio.token -l 100 -L <上页more值>小结
cleos get scope是 eos 生态中快速掌握合约数据布局的入口级命令:它由 programs/cleos/main.cpp 定义 CLI 参数,通过POST /v1/chain/get_table_by_scope(注册于 plugins/chain_api_plugin/chain_api_plugin.cpp)转发至链插件,由 chain_plugin.cpp 中基于by_code_scope_table索引的区间遍历完成查询。掌握--table/--lower/--upper/--limit/--reverse五个选项的组合使用,以及rows/more输出字段的分页语义,即可高效地探查任意合约的存储结构,并为后续的get table精确数据读取铺平道路。
- 区块链
【免费下载链接】eos
An open source smart contract platform
相关推荐
使用 cleos get table 查询 EOS 智能合约表数据:从入门到源码级解析
使用 cleos get table 查询 EOS 智能合约表数据:从入门到源码级解析 本篇指南围绕 EOS 区块链的核心数据检索操作展开:通过命令行工具 cl
区块链用 LQL 编写 BigQuery 日志查询:skills29 cloud-logging-query-generation 技能中的查询参考与实战模式
用 LQL 编写 BigQuery 日志查询:skills29 cloud logging query generation 技能中的查询参考与实战模式 Big
区块链eos 节点验证:cleos get schedule 命令详解与生产者调度表(Producer Schedule)查询实战
eos 节点验证:cleos get schedule 命令详解与生产者调度表(Producer Schedule)查询实战 导读 在 EOSIO 智能合约平台
区块链
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考