一、代码整体架构
主要包括应用层、插件层、库函数层和智能合约层。代码整体架构:
1.1 programs(应用层):
cloes:
nodeos:服务器端,也就是区块生产节点,用于接受客户端的远端请求,并打包区块,主要包含四个插件,chain_plugin、http_plugin、net_plugin、producer_plugin。
keosd:钱包管理模块,主要包括三个插件,wallet_plugin、wallet_api_plugin、http_plugin。
1.2 plugins(插件层)
支持动态加载相关组件,实现了应用层的业务逻辑和区块链底层实现的解耦,同时为应用开发者提供友好的API接口,比较重要的有以下几个插件:chain_plugin、http_plugin
1.3 libraries(库函数层)
为应用层和插件层提供基础能力,实现了区块链的底层关键技术,例如,交易处理,生产区块,加密功能,文件IO操作,网络通信能力等
1.4 constracts(智能合约层)
主要包含一些智能合约的示例代码。
二、programs(应用层)
2.1 nodeos:
位置:programs/nodeos/main.cpp
从main函数开始,程序大致分为三部分:选项配置、加载插件、启动程序。加载chain_plugin, http_plugin, net_plugin, producer_plugin插件
int main(int argc, char** argv)
{
try {
//选项配制
app().set_version(eosio::nodeos::config::version);
app().register_plugin<history_plugin>();
auto root = fc::app_path();
app().set_default_data_dir(root / "eosio/nodeos/data" );
app().set_default_config_dir(root / "eosio/nodeos/config" );
//加载插件
if(!app().initialize<chain_plugin, http_plugin, net_plugin, producer_plugin>(argc, argv))
return INITIALIZE_FAIL;
initialize_logging();
ilog("nodeos version ${ver}", ("ver", eosio::utilities::common::itoh(static_cast<uint32_t>(app().version()))));
ilog("eosio root is ${root}", ("root", root.string()));
//启动程序
app().startup();
app().exec();
} catch( const extract_genesis_state_exception& e ) {
return EXTRACTED_GENESIS;
2.2 cleos
(1)位置:programs/cleos/main.cpp
cleos是一个命令行工具,用于和区块链数据交互以及管理钱包,从main函数开始,
程序大致分为三部分:创建主命令和选项、创建子命令和选项、解析用户参数后调用对应命令的回调函数。
//创建主命令,并添加主选项
CLI::App app{"Command Line Interface to EOSIO Client"};
app.require_subcommand();
app.add_option( "-H,--host", obsoleted_option_host_port, localized("the host where nodeos is running") )->group("hidden");
app.add_option( "-p,--port", obsoleted_option_host_po
....
....
//创建子命令
auto version = app.add_subcommand("version", localized("Retrieve version information"), false);
version->require_subcommand();
version->add_subcommand("client", localized("Retrieve version information of the client"))->set_callback([] {
std::cout << localized("Build version: ${ver}", ("ver", eosio::client::config::version_str)) << std::endl;
});
// 为主命令创建create子命令
auto create_key = create->add_subcommand("key", localized("Create a new keypair and print the public and private keys"))->set_callback( [&r1](){
if( r1 ) {
auto pk = private_key_type::generate_r1();
auto privs = string(pk);
auto pubs = string(pk.get_public_key());
std::cout << localized("Private key: ${key}", ("key", privs) ) << std::endl;
std::cout << localized("Public key: ${key}", ("key", pubs ) ) << std::endl;
} else {
auto pk = private_key_type::generate();
auto privs = string(pk);
auto pubs = string(pk.get_public_key());
std::cout << localized("Private key: ${key}", ("key", privs) ) << std::endl;
std::cout << localized("Public key: ${key}", ("key", pubs ) ) << std::endl;
}
});
create_key->add_flag( "--r1", r1, "Generate a key using the R1 curve (iPhone), instead of the K1 curve (Bitcoin)" );
// 为create子命令创建子命令account
auto createAccount = create_account_subcommand( create, true /*simple*/ );
...
...
//解析用户命令参数,调用对应的回调函数
try {
app.parse(argc, argv);
} catch (const CLI::ParseError &e) {
return app.exit(e);
} catch (const explained_exception& e) {
return 1;
} catch (connection_exception& e) {
if (verbose_errors) {
elog("connect error: ${e}", ("e", e.to_detail_string()));
}
} catch (const fc::exception& e) {
// attempt to extract the error code if one is present
if (!print_recognized_errors(e, verbose_errors)) {
// Error is not recognized
if (!print_help_text(e) || verbose_errors) {
elog("Failed with error: ${e}", ("e", verbose_errors ? e.to_detail_string() : e.to_string()));
}
}
return 1;
}
(2) 创建命令
位置:eos/programs/cleos/CLI11.hpp
//创建主命令
Option *add_option(std::string name, callback_t callback, std::string description = "", bool defaulted = false) {
Option myopt{name, description, callback, defaulted, this};
if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) {
return *v == myopt;
}) == std::end(options_)) {
options_.emplace_back();
Option_p &option = options_.back();
option.reset(new Option(name, description, callback, defaulted, this));
return option.get();
} else
throw OptionAlreadyAdded(myopt.get_name());
}
//创建子命令
/// Add a subcommand. Like the constructor, you can override the help message addition by setting help=false
App *add_subcommand(std::string name, std::string description = "", bool help = true) {
subcommands_.emplace_back(new App(description, help, detail::dummy));
subcommands_.back()->name_ = name;
subcommands_.back()->allow_extras();
subcommands_.back()->parent_ = this;
subcommands_.back()->ignore_case_ = ignore_case_;
subcommands_.back()->fallthrough_ = fallthrough_;
for(const auto &subc : subcommands_)
if(subc.get() != subcommands_.back().get())
if(subc->check_name(subcommands_.back()->name_) || subcommands_.back()->check_name(subc->name_))
throw OptionAlreadyAdded(subc->name_);
return subcommands_.back().get();
}
(3)解析用户参数
设置完所有的命令、选项和回调函数后,开始解析用户输入的参数,并匹配到对应的命令,执行相应功能。
try {
app.parse(argc, argv);
} catch (const CLI::ParseError &e) {
return app.exit(e);
} catch
将用户参数解析后保存在std::vector args中,通过parse(args)做进一步解析:
eos/programs/cleos/CLI11.hpp
/// Parses the command line - throws errors
/// This must be called after the options are in but before the rest of the program.
std::vector<std::string> parse(int argc, char **argv) {
name_ = argv[0];
std::vector<std::string> args;
for(int i = argc - 1; i > 0; i--)
args.emplace_back(argv[i]);
return parse(args);
}
2.3 keosd
keosd钱包管理模块的处理流程,从main 函数开始,程序大致分为三部分:选项配置、加载插件、启动程序,主要的功能由wallet_plugin、wallet_api_plugin、http_plugin这三个插件完成。
位置:programs/keosd/main.cpp