程序入口
首先是节点的启动过程,/home/pct/workspace/eos/eos/programs/nodeos/main.cpp,这里启动了节点。 其次是客户端的启动过程,/home/pct/workspace/eos/eos/programs/cleos/main.cpp。
合约跟踪
我们的目的是解析EOS合约部分的调用,因此从一个合约部署到调用的流程来读代码,首先是合约部署,我们在上篇文章中看到部署一个合约调用的命令为:
./cleos set contract eosio.token ../../contracts/eosio.token -p eosio.token
调用一个合约执行的命令为:
./cleos push action eosio.token create '[ "eosio", "1000000000.0000 SYS"]' -p eosio.token
我们就从这两个命令入手。
部署合约和调用
部署的客户端代码为:
// set subcommand
auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state"));
setSubcommand->require_subcommand();
auto contractSubcommand = setSubcommand->add_subcommand("contract", localized("Create or update the contract on an account"));
contractSubcommand->add_option("account", account, localized("The account to publish a contract for"))
->required();
contractSubcommand->add_option("contract-dir", contractPath, localized("The path containing the .wast and .abi"))
->required();
contractSubcommand->add_option("wast-file", wastPath, localized("The file containing the contract WAST or WASM relative to contract-dir"));
// ->check(CLI::ExistingFile);
auto abi = contractSubcommand->add_option("abi-file,-a,--abi", abiPath, localized("The ABI for the contract relative to contract-dir"));
add_standard_transaction_options(contractSubcommand, "account@active");
add_standard_transaction_options(codeSubcommand, "account@active");
add_standard_transaction_options(abiSubcommand, "account@active");
contractSubcommand->set_callback([&] {
shouldSend = false;
set_code_callback();
set_abi_callback();
std::cout << localized("Publishing contract...") << std::endl;
send_actions(std::move(actions), 10000, packed_transaction::zlib);
});
codeSubcommand->set_callback(set_code_callback);
abiSubcommand->set_callback(set_abi_callback);
设置了两个回调:
std::vector<chain::action> actions;
auto set_code_callback = [&]() {
std::string wast;
fc::path cpath(contractPath);
if( cpath.filename().generic_string() == "." ) cpath = cpath.parent_path();
if( wastPath.empty() )
{
wastPath = (cpath / (cpath.filename().generic_string()+".wasm")).generic_string();
if (!fc::exists(wastPath))
wastPath = (cpath / (cpath.filename().generic_string()+".wast")).generic_string();
}
std::cout << localized(("Reading WAST/WASM from " + wastPath + "...").c_str()) << std::endl;
fc::read_file_contents(wastPath, wast);
FC_ASSERT( !wast.empty(), "no wast file found ${f}", ("f", wastPath) );
vector<uint8_t> wasm;
const string binary_wasm_header("\x00\x61\x73\x6d", 4);
if(wast.compare(0, 4, binary_wasm_header) == 0) {
std::cout << localized("Using already assembled WASM...") << std::endl;
wasm = vector<uint8_t>(wast.begin(), wast.end());
}
else {
std::cout << localized("Assembling WASM...") << std::endl;
wasm = wast_to_wasm(wast);
}
actions.emplace_back( create_setcode(account, bytes(wasm.begin(), wasm.end()) ) );
if ( shouldSend ) {
std::cout << localized("Setting Code...") << std::endl;
send_actions(std::move(actions), 10000, packed_transaction::zlib);
}
};
auto set_abi_callback = [&]() {
fc::path cpath(contractPath);
if( cpath.filename().generic_string() == "." ) cpath = cpath.parent_path();
if( abiPath.empty() )
{
abiPath = (cpath / (cpath.filename().generic_string()+".abi")).generic_string();
}
FC_ASSERT( fc::exists( abiPath ), "no abi file found ${f}", ("f", abiPath) );
try {
actions.emplace_back( create_setabi(account, fc::json::from_file(abiPath).as<abi_def>()) );
} EOS_RETHROW_EXCEPTIONS(abi_type_exception, "Fail to parse ABI JSON")
if ( shouldSend ) {
std::cout << localized("Setting ABI...") << std::endl;
send_actions(std::move(actions), 10000, packed_transaction::zlib);
}
};
第一个回调调用send_actions(std::move(actions), 10000, packed_transaction::zlib);来将合约发送,第二个回调同样调用send_actions(std::move(actions), 10000, packed_transaction::zlib);将字节发送。 第一个回调读取的是wasm文件,第二个则是读取abi文件,abi是二进制通用接口,为什么要调用此文件还不清楚。
回调函数将wasm文件读取成字节流,然后打包进action结构体中,通过send发送,send函数如下:
void send_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
auto result = push_actions( move(actions), extra_kcpu, compression);
调用了push_actions,
fc::variant push_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu, packed_transaction::compression_type compression = packed_transaction::none ) {
signed_transaction trx;
trx.actions = std::forward<decltype(actions)>(actions);
return push_transaction(trx, extra_kcpu, compression);
}
最后调用call发送:
return call(push_txn_func, packed_transaction(trx, compression));
call函数则调用了http协议将消息发送到节点:
return eosio::client::http::do_http_call( *cp, fc::variant(v) );
端口为:
string url = "http://localhost:8888/";
这个就是客户端的合约部署流程,调用流程和部署类似,只是发送的消息内容不同而已。