EOSIO nodeos history_plugin 完全指南:历史数据缓存层、过滤器配置与迁移方案
2026/9/23 17:21:35 网站建设 项目流程

EOSIO nodeos history_plugin 完全指南:历史数据缓存层、过滤器配置与迁移方案

【免费下载链接】eosAn open source smart contract platform项目地址: https://gitcode.com/gh_mirrors/eo/eos

导读

本文围绕 EOSIO 节点(nodeos)的history_plugin展开,讲解其作为区块链对象历史数据缓存层的定位、--filter-on/--filter-out过滤器的完整配置语法,并结合仓库源码剖析其链上状态数据库索引、动作追踪与账号历史记录的底层实现,最后给出该插件已弃用后的官方迁移路线(state_history_plugintrace_api_plugin)。读完本文,你将能够正确启用并配置历史追踪、理解cleos get系列历史查询命令背后的数据来源,并掌握在历史功能演进中平滑升级的方案。

插件概述:历史数据的缓存层

根据 官方文档,history_plugin为区块链对象提供获取历史数据的缓存层(cache layer),它本身不产生链数据,而是依赖核心的chain_plugin提供数据来源。

从源码看,该插件的头文件 history_plugin.hpp 给出了更精确的职责描述:

该插件追踪一组被配置账号关联的所有动作与密钥,使钱包(wallet)能够分页查询历史记录。一个动作只要满足以下任一条件就会被纳入账号历史:receiver(接收者);或出现在 auth(授权)列表中的任一账号。一个密钥只要出现在 updateauth 或 newaccount 的权限(authorities)中,就会被关联到对应账号。

因此,history_plugin承担两类历史数据的采集:

  1. 动作历史(action history):按账号维度记录与之相关的动作执行轨迹;
  2. 账号关联历史:记录「公钥 ↔ 账号 ↔ 权限」以及「被控账号 ↔ 控制账号」的关联关系,用于反向查询。

⚠️ 注意:插件依赖声明APPBASE_PLUGIN_REQUIRES((chain_plugin))(见 history_plugin.hpp),即启动时强制要求chain_plugin已加载,否则抛出missing_chain_plugin_exception

弃用声明与迁移方向

官方文档在插件页顶部明确标注了Deprecation Notice

history_plugin已弃用,不再维护。请改用state_history_plugintrace_api_plugin

这意味着:

  • 新部署的节点不应再启用history_plugin
  • 存量节点应规划迁移,state_history_plugin提供基于状态历史日志的完整历史回放能力,trace_api_plugin则提供基于分块文件的交易追踪 API;
  • 仓库源码中同样留下了对应的注释佐证——history_plugin.cpp 中的 TODO 注释明确写着「应使用独立的 chainbase 数据库管理 history_plugin 的状态(或直接移除已弃用的 history_plugin)」;而 history_api_plugin.cpp 中get_transaction的 RPC 注册行也被注释掉,仅保留了其余三个端点。

启用方式

history_plugin支持在config.ini配置文件中启用,也可通过nodeos命令行参数启用。

config.ini 方式:

# config.ini plugin = eosio::history_plugin [options]

命令行方式:

nodeos ... --plugin eosio::history_plugin [options]

其中[options]即下文将要展开的两个过滤器选项。

依赖与配套插件

history_plugin的数据查询能力需要配合 RPC 接口插件一起使用才能对外提供 HTTP 服务。官方文档的 Dependencies 一节仅列出了chain_plugin,但从完整的查询链路来看,实际部署通常还包含:

插件作用说明
chain_plugin链数据源硬性依赖,history_plugin从中读取区块与动作
history_api_plugin暴露 RPC 端点history_plugin的只读查询包装为/v1/history/*HTTP 接口
http_pluginHTTP 服务history_api_plugin通过它注册路由

参考 history_api_plugin 文档中的依赖加载示例,完整的启用组合如下:

config.ini:

plugin = eosio::history_plugin plugin = eosio::chain_plugin plugin = eosio::http_plugin plugin = eosio::history_api_plugin

命令行:

nodeos ... \ --plugin eosio::history_plugin [options] \ --plugin eosio::chain_plugin [operations] [options] \ --plugin eosio::http_plugin [options] \ --plugin eosio::history_api_plugin

配置选项:--filter-on--filter-out

插件仅有两个配置项,均可在nodeos命令行或config.ini中指定(源码中二者都注册在cfg.add_options()下,见 history_plugin.cpp):

Config Options for eosio::history_plugin: -f [ --filter-on ] arg Track actions which match receiver:action:actor. Actor may be blank to include all. Action and Actor both blank allows all from Recieiver. Receiver may not be blank. -F [ --filter-out ] arg Do not track actions which match receiver:action:actor. Action and Actor both blank excludes all from Reciever. Actor blank excludes all from reciever:action. Receiver may not be blank.

过滤器语法:receiver:action:actor

两个选项的值都采用receiver:action:actor三段式三元组,语义如下:

含义是否可为空
receiver动作接收者(合约账号)不可为空
action动作名称可为空(表示该 receiver 的所有动作)
actor授权执行者(auth 列表中的账号)可为空(表示不区分执行者)

具体匹配规则(源码 history_plugin.cpp 中的filter()函数实现):

  • receiver:action:actor全字段匹配:动作的 receiver、动作名、authorization 中的 actor 全部对应;
  • receiver:action:(actor 空):匹配该 receiver 下指定动作名的所有动作,不区分执行者;
  • receiver::(action、actor 均空):匹配该 receiver 发出的所有动作;
  • receiver::actor(action 空):匹配该 receiver 下由指定 actor 授权的所有动作。

判定顺序:先应用filter_on白名单——只要命中任一filter_on规则则视为「应追踪」;若filter_on为空(未配置)则默认全部通过;随后应用filter_out黑名单——一旦命中任一filter_out规则则立即排除。

通配符*与共享内存风险

filter-on支持特殊值*(或"*"),等价于开启bypass_filter,即不经过任何过滤、追踪全部动作。源码 history_plugin.cpp 中对此有明确的告警日志:

--filter-on * enabled. This can fill shared_mem, causing nodeos to stop.

由于history_plugin将所有追踪结果写入与链状态共享的 chainbase 共享内存数据库(shared_mem),全量追踪会持续消耗内存直至写满,导致 nodeos 停止运行。因此*通配符仅适合小规模测试网络,生产环境务必使用精确的receiver:action:actor三元组来控制数据量。

参数解析与合法性校验

源码 history_plugin.cpp 展示了参数解析的细节:

  • 每个filter-on/filter-out值会按:分割,EOS_ASSERT强制要求恰好分为 3 段,否则抛出invalid_arg_exception
  • receiver 段解析后不能为空账号名(fe.receiver.to_uint64_t()必须非零),同样会报Invalid value ... for --filter-on/--filter-out
  • 两个选项均为bpo::value<vector<string>>()->composing(),即可以重复指定多次,构成过滤器集合(std::set<filter_entry>,以(receiver, action, actor)为 key 排序去重,见 history_plugin.cpp)。

config.ini 示例:

# 只追踪 eosio.token 的 transfer 动作 filter-on = eosio.token:transfer: # 追踪 eosio.system 发给任意账号的所有动作 filter-on = eosio.system:: # 额外追踪 eosio.token 由 alice 授权的所有动作 filter-on = eosio.token::alice # 排除 eosio.system 的 setcode 动作 filter-out = eosio.system:setcode:

命令行示例:

nodeos ... \ --plugin eosio::history_plugin \ -f eosio.token:transfer: \ -f eosio.system:: \ -F eosio.system:setcode:

底层实现:如何把历史写入链状态库

理解history_plugin的最佳方式是阅读其核心实现 history_plugin.cpp。插件初始化时(plugin_initialize)会向 chainbase 数据库注册四类索引:

索引对象类型用途
account_history_indexaccount_history_object记录「账号 → 动作」的每账号序列号(account_sequence_num)
action_history_indexaction_history_object记录每个被追踪动作的完整轨迹(打包的 action_trace、区块号、区块时间、交易 ID、全局序列号)
account_control_history_multi_indexaccount_control_history_object记录「被控账号/权限 ↔ 控制账号」关系
public_key_history_multi_indexpublic_key_history_object记录「公钥 ↔ 账号/权限」关系

其中前两类索引定义见 history_plugin.cpp:

  • account_history_object:包含账号名、全局动作序列号(action_sequence_num,取自act.receipt->global_sequence)与每账号序列号(account_sequence_num);
  • action_history_object:将action_trace通过fc::raw::pack序列化进packed_action_trace(shared_string),并记录block_numblock_timetrx_id

数据采集调用链

插件在plugin_initialize中订阅了链的applied_transaction信号(history_plugin.cpp),每当一笔交易被应用,就触发如下调用链:

applied_transaction 信号 └─ on_applied_transaction(transaction_trace) # 仅处理 executed / soft_fail 状态 └─ on_action_trace(action_trace) # 逐个动作应用过滤器 ├─ filter(action_trace) # filter_on / filter_out 判定 ├─ 写入 action_history_object # 打包完整 action_trace ├─ account_set(action_trace) # 收集 receiver + auth 列表中的账号 └─ record_account_action(account) # 为每个账号写 account_history_object

关键细节:

  • 状态过滤on_applied_transaction仅处理executed(成功执行)与soft_fail(软失败)两类交易收据,其余状态直接忽略(history_plugin.cpp);
  • 系统动作特殊处理on_action_trace中若 receiver 为系统账号(eosio),会调用on_system_action(history_plugin.cpp)——对newaccount将 owner/active 权限中的公钥与账号写入密钥历史与账号控制历史;对updateauth先删除旧权限记录再写入新记录;对deleteauth删除对应权限记录。这正是get_key_accounts/get_controlled_accounts查询的数据来源;
  • 账号序列号自增record_account_action通过查找该账号已有记录的最大account_sequence_num并 +1 生成新序列号(history_plugin.cpp),保证每个账号的历史可按序号分页;
  • 写入方式警示:源码多处通过const_cast<chainbase::database&>(chain.db())覆盖链状态库的只读访问直接写入,并注释「Override read-only access to state DB (highly unrecommended practice!)」——即 history 数据与链状态共用同一数据库文件,这也是其弃用的重要原因之一。

对外查询能力与 cleos 对应关系

history_plugin本身仅提供内部只读 API(history_apis::read_only,声明见 history_plugin.hpp),需通过history_api_plugin暴露为 HTTP RPC。参考 history_api_plugin.cpp,注册的端点为:

RPC 端点功能对应 cleos 命令
POST /v1/history/get_actions分页查询某账号的动作历史cleos get actions
POST /v1/history/get_transaction按交易 ID 查询交易详情(含所有动作轨迹)cleos get transaction
POST /v1/history/get_key_accounts查询某公钥关联的所有账号cleos get accounts
POST /v1/history/get_controlled_accounts查询某账号控制(作为控制方)的所有账号cleos get servants

get_actions:分页机制

get_actions请求参数为account_namepos(绝对序列位置,-1 表示最后一个动作)与offset(相对 pos 的动作数量)。源码 history_plugin.cpp 说明了分页规则:

  • 未指定pos时默认为 -1,即从末尾向前翻页;
  • offset > 0时返回区间[pos, pos+offset)offset < 0时返回区间[pos+offset, pos)(cleos 默认offset = -20);
  • 查询会先定位account_history_index中该账号的序列号区间,再通过action_history_index反查action_history_object并解包动作轨迹;
  • 每次查询限时 100ms(fc::microseconds(100000)),超时则设置time_limit_exceeded_error并中断,防止长历史账号拖垮节点。

get_transaction:ID 前缀匹配与区块提示

get_transaction接受交易 ID 与可选的block_num_hint。源码 history_plugin.cpp 的特点包括:

  • 交易 ID 支持十六进制前缀匹配(至少 8 个字符),可只提供 ID 前缀即可定位交易;
  • 若历史库中未命中且未提供block_num_hint,抛出tx_not_found
  • 提供block_num_hint时,会直接从指定区块的交易收据中查找匹配交易,实现「无历史库也能查交易」的回退路径;
  • 返回结果包含idblock_numblock_timelast_irreversible_block、完整trx(含签名与上下文自由数据)以及traces(所有动作轨迹)。

cleos 命令示例

# 查询 alice 最近的 20 条动作(默认从末尾倒序) cleos get actions alice # 查询 alice 第 10 到第 29 条动作 cleos get actions alice 10 20 # 查询某交易的完整信息(可携带区块号提示加速) cleos get transaction 9d2f3c... -b 123456 # 查询某公钥关联的所有账号 cleos get accounts EOS5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # 查询某个控制账号下属的所有账号 cleos get servants eosio

注意:cleos get actions的格式化输出实现见 main.cpp,其中#前缀表示该动作已不可逆(block_num <= last_irreversible_block),?表示尚在可逆区间;--json/--full/--pretty/--console可控制输出详细程度。

迁移到 state_history_plugin 与 trace_api_plugin

由于history_plugin已弃用,新项目应直接采用官方推荐的替代方案:

  • state_history_plugin:将完整链状态与动作历史以追加日志形式写入磁盘,支持从快照启动、全量历史重放等场景,适合需要完整可追溯历史的应用。仓库文档提供了无历史快速启动、全量历史重放/重新同步、带全量历史创建快照等完整操作流程;
  • trace_api_plugin:按分块文件提供高性能的交易追踪 REST API,附带trace_api_util工具,适合索引服务与区块浏览器等对读取性能要求高的场景。

迁移时需注意:history_plugin将数据写入与链状态共享的 chainbase 数据库,历史不会自动补录;若需保留旧历史,需在切换前通过cleos get actions等方式导出,或在测试网络中重新启动并同步以生成新格式的历史数据。

小结

history_plugin是 EOSIO 早期生态中用于提供账号历史查询的关键插件:它以receiver:action:actor过滤器控制采集范围,将动作轨迹与账号/公钥关联关系写入链状态共享内存,再经由history_api_plugin对外提供get_actionsget_transactionget_key_accountsget_controlled_accounts四类查询。虽然官方已将其标记为弃用并推荐迁移至state_history_plugin/trace_api_plugin,但理解其过滤器语义、分页模型与数据组织方式,依然有助于快速上手cleos get历史命令,并为理解新一代历史方案的设计动机提供背景。

【免费下载链接】eosAn open source smart contract platform项目地址: https://gitcode.com/gh_mirrors/eo/eos

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询