Edit me

程序入口

首先是节点的启动过程,/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/";

这个就是客户端的合约部署流程,调用流程和部署类似,只是发送的消息内容不同而已。

节点启动

Tags: