BitShares(比特股)是一种支持包括虚拟货币、法币、贵金属等有价值实物的开源分布式交易系统。关于BitShares,网上的资料有很多,这里不作具体赘述,本文主要对BitShares的插件的开发进行入门介绍。
在进行BitShares的插件开发前,需要具备以下技能:
- 本地编译并搭建过BitShares节点/重钱包(必须)
- 能看懂简单的C++代码(必须)
- 使用过命令行版的钱包,或自已构造过json消息和节点进行简单的交互(可选)
推荐一下这个链接:BitShares (BTS) 中文技术文档,对于BitShares技术学习非常有帮助。
1. 目的
通过插件,可以直接获取到BitShares区块链上的各种数据,对BitShares进行扩展开发,实现定制化的功能。本文通过插件的方式来监控区块链上的转账操作,对插件的开发进行简单的介绍。
2. 目录结构
BitShares的插件源代码存放在libraries/plugins
目录中,以我们要创建的monitor
插件为例,新建的插件目录结构如下:
libraries/plugins/
├── monitor
│ ├── CMakeLists.txt (+)
│ ├── include
│ │ └── graphene
│ │ └── monitor
│ │ └── monitor.hpp (+)
│ └── monitor.cpp (+)
└── CMakeLists.txt (*)
其中,(+)
表示新增的文件,(*)
表示需要修改的文件。
3. 插件接口
插件需要实现的接口如下示例代码。
#include <graphene/app/plugin.hpp>
namespace graphene { namespace monitor_plugin {
// 插件必须继承自graphene::app::plugin类
class monitor_plugin : public graphene::app::plugin {
public:
// 插件名称
std::string plugin_name()const override;
// 插件描述信息
std::string plugin_description()const override;
// 插件配置解析接口
virtual void plugin_set_program_options(
boost::program_options::options_description &command_line_options,
boost::program_options::options_description &config_file_options
) override;
// 插件初始化
virtual void plugin_initialize(
const boost::program_options::variables_map& options
) override;
// 启动插件
virtual void plugin_startup() override;
// 停止插件
virtual void plugin_shutdown() override;
};
} } // graphene::monitor_plugin
4. 接口实现示例
4.1 插件名称
即启动节点时指定的插件名称,示例中插件名称是monitor
,启动节点的时候如果希望加载该插件,则需要带上--plugins "monitor"
的参数。
std::string monitor_plugin::plugin_name()const
{
return "monitor";
}
4.2 插件描述信息
描述信息在查看帮助信息的时候会显示出来。
std::string monitor_plugin::plugin_description()const
{
return "Monitor transaction status.";
}
4.3 插件配置解析接口
注册插件的参数选项,这里使用了boost库,包括参数名称、参数类型、参数描述。参数支持两种配置方式,一种是命令行参数,另一种是配置文件,两种方式需要分别注册,代码示例如下。
#define MONITOR_OPT_ACTION_TYPE "monitor-action-type"
void monitor_plugin::plugin_set_program_options(
boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options)
{
command_line_options.add_options()
(MONITOR_OPT_ACTION_TYPE, boost::program_options::value<uint32_t>(),
"The type of operation monitored");
config_file_options.add(command_line_options);
}
4.4 插件初始化
插件初始化接口中,一般用来读取参数配置,或者需要全局初始化不释放的资源。
#define MONITOR_OPT_ACTION_TYPE "monitor-action-type"
#define MONITOR_ACTION_TYPE_ALL (0xFFFFFFFF)
void monitor_plugin::plugin_initialize(const boost::program_options::variables_map& options) {
try {
ilog("monitor plugin: plugin_initialize() begin");
// 读取参数配置
if (options.count(MONITOR_OPT_ACTION_TYPE)) {
monitor_action_type = options[MONITOR_OPT_ACTION_TYPE].as<uint32_t>();
} else {
monitor_action_type = MONITOR_ACTION_TYPE_ALL;
}
ilog("monitor plugin: plugin_initialize() end");
} FC_LOG_AND_RETHROW()
return;
}
4.5 启动插件
本例中,启动插件时将回调函数monitor_singed_block
注册到了生成区块的事件中applied_block
,为后续监控打包区块数据做准备。
void monitor_plugin::plugin_startup() {
// 注册事件回调函数
monitor_signed_block_handler = database().applied_block.connect(
[&]( const graphene::chain::signed_block& blk ) {
monitor_signed_block(blk);
}
);
return;
}
4.6 停止插件
和启动相对应,将前面注册的事件取消掉,停止监控。
void monitor_plugin::plugin_shutdown() {
// 卸载事件回调函数
database().applied_block.disconnect(monitor_signed_block_handler);
return;
}
4.7 业务处理函数
这里业务处理函数是作为回调函数注册到生成区块的事件上的,所实现的功能是遍历新生成区块上的所有操作,并将转帐类型的操作打印出来,具体代码如下。
void monitor_plugin::monitor_signed_block(const graphene::chain::signed_block& blk) {
for (uint32_t i = 0; i < blk.transactions.size(); ++i) {
processed_transaction trans = blk.transactions[i];
for (uint32_t j = 0; j < trans.operations.size(); ++j) {
operation op = trans.operations[j];
if (operation::tag<transfer_operation>::value == op.which()) {
transfer_operation& op_trans = op.get<transfer_operation>();
const account_object& from_account = op_trans.from(database());
const account_object& to_account = op_trans.to(database());
const asset_object& asset_type = op_trans.amount.asset_id(database());
cout<<">>> action transfer: "<< from_account.name
<< " -> " << to_account.name
<< " " << asset_type.amount_to_pretty_string(op_trans.amount)
<< endl;
}
}
}
return;
}
5. 插件注册
插件写完了,还需要注册到系统中才行,在main
函数中可以找到类似如下代码:
int main(int argc, char** argv) {
...
auto witness_plug = node->register_plugin<witness_plugin::witness_plugin>();
auto debug_witness_plug = node->register_plugin<debug_witness_plugin::debug_witness_plugin>();
auto history_plug = node->register_plugin<account_history::account_history_plugin>();
auto elasticsearch_plug = node->register_plugin<elasticsearch::elasticsearch_plugin>();
auto market_history_plug = node->register_plugin<market_history::market_history_plugin>();
auto delayed_plug = node->register_plugin<delayed_node::delayed_node_plugin>();
auto snapshot_plug = node->register_plugin<snapshot_plugin::snapshot_plugin>();
auto es_objects_plug = node->register_plugin<es_objects::es_objects_plugin>();
auto grouped_orders_plug = node->register_plugin<grouped_orders::grouped_orders_plugin>();
...
}
这里我们将monitor_plugin
插件加上去,增加下面代码完成插件的注册:
int main(int argc, char** argv) {
...
auto monitor_plug = node->register_plugin<monitor_plugin::monitor_plugin>();
...
}
6. 编译
BitShares源代码使用cmake编译,需要在插件目录下新增一个CMakeLists.txt
文件,内容如下。
file(GLOB HEADERS "include/graphene/monitor/*.hpp")
add_library( graphene_monitor
monitor.cpp
)
target_link_libraries( graphene_monitor graphene_chain graphene_app )
target_include_directories( graphene_monitor
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
install( TARGETS
graphene_monitor
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
另外,还需要将上级目录中的CMakeLists.txt
中追加新插件目录:
...
add_subdirectory( monitor )
完成插件后,在BitShares目录中依次执行cmake
、make
,重新编译witness_node
程序。
cmake
make
7. 运行
启动witness_node
时通过命令行参数加载monitor
插件,执行如下命令:
./witness_node --plugins "monitor"
成功运行后,当区块链上发生转帐操作时,可以看到命令行中会打印追踪到的交易信息。
8. 完整的源代码
本地下载:monitor.hpp、monitor.cpp
Github:点击这里,可以直接查看完整的示例代码。
Comments