news 2026/9/24 3:06:52

eos 智能合约作用域查询指南:`cleos get scope` 命令详解与源码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
eos 智能合约作用域查询指南:`cleos get scope` 命令详解与源码解析
  • 区块链

【免费下载链接】eos

An open source smart contract platform

项目地址:https://gitcode.com/gh_mirrors/eo/eos
点击查看免费下载

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 组合,相当于先"侦察"合约的存储布局,再决定去读取哪张表。两者搭配使用是排查合约数据的最常用组合拳。

位置参数

参数类型是否必填说明
contractTEXT拥有这些表的合约账户名,即表的所有者(对应 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, --tableTEXT表名过滤条件,仅返回指定名称的表,可选
-l, --limitUINT返回的最大行数,可选;服务端默认值为 10
-L, --lowerTEXTscope 的下界(lower bound),可选,用于范围查询与翻页
-U, --upperTEXTscope 的上界(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中,其关键逻辑如下:

  1. 构造查询区间:以(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); }
  1. 区间有效性检查:若上界小于下界,直接返回空结果(此时more为空):
if( upper_bound_lookup_tuple < lower_bound_lookup_tuple ) return result;
  1. 遍历表索引:通过table_id_multi_indexby_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 ); }
  1. 行数上限与游标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();)。

  2. 表名过滤table_receiver::add_table_rowif( 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.system

2. 结合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 accounts

3. 精确过滤与范围查询

只想看某个合约下名为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 -r

4. 大数据量翻页

当合约作用域数量超过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

项目地址:https://gitcode.com/gh_mirrors/eo/eos
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 3:03:12

GLM 5.3 Batch 模式高效应用指南

在处理海量数据时&#xff0c;很多开发者最先遇到的瓶颈往往不是算法不够先进&#xff0c;而是工程架构无法支撑高并发下的吞吐量。想象一下&#xff0c;当你需要清洗百万级的用户评论、将成千上万份技术文档翻译成多国语言&#xff0c;或者为智能客服构建覆盖全业务线的知识库…

作者头像 李华
网站建设 2026/9/24 2:54:52

CH341A串口与I2C资源冲突原理及工程解决方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 2:52:43

云边端三层架构实战:边缘计算自治设计与部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 2:50:41

Autosar CANTP六大超时参数深度解析与实战调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华