EOS源码解析---通信机制

1、通信流程
客户端和服务器端的通信采用RESTful软件架构风格,服务器端的每个资源对应一个唯一的URL地址,客户端将URL地址封装成http请求发送到服务器端,请求对应的资源或者执行相应操作。
通信流程
cleos与nodeos交互
2、客户端发送消息
programs\cleos\main.cpp

/**
 (1)在main函数中,解析transfer命令,通过create_transfer函数将交易发送者、交易接收者、token数量等信息封装成mutable_variant_object对象,
 (2)调用send_action函数,将交易信息发送到服务器端打包进区块链
 */
send_actions({create_transfer(con,sender, recipient, to_asset(amount), memo)});
//send_actions函数
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);
}

(3)send_actions函数处理

//(1)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);
}
//(2)push_transaction:发送URL地址到服务器端
fc::variant push_transaction( signed_transaction& trx, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
   auto info = get_info();
   trx.expiration = info.head_block_time + tx_expiration;

   // Set tapos, default to last irreversible block if it's not specified by the user
   block_id_type ref_block_id = info.last_irreversible_block_id;
   // 发送 ”/V1/chain/push_transaction” URL地址到服务器端
   try {
      fc::variant ref_block;
      if (!tx_ref_block_num_or_id.empty()) {
         ref_block = call(get_block_func, fc::mutable_variant_object("block_num_or_id", tx_ref_block_num_or_id));
         ref_block_id = ref_block["id"].as<block_id_type>();
      }
 //(3)     
template<typename T>
fc::variant call( const std::string& url,
                  const std::string& path,
                  const T& v ) {
   try {
      eosio::client::http::connection_param *cp = new eosio::client::http::connection_param(context, parse_url(url) + path,
              no_verify ? false : true, headers);

      return eosio::client::http::do_http_call( *cp, fc::variant(v), print_request, print_response );
   }
   catch(b
  //(4)programs\cleos\httpc.cpp
     fc::variant do_http_call( const connection_param& cp,
                             const fc::variant& postdata,
                             bool print_request,
                             bool print_response ) {
   std::string postjson;
   if( !postdata.is_null() ) {
      postjson = print_request ? fc::json::to_pretty_string( postdata ) : fc::json::to_string( postdata );
   }

   const auto& url = cp.url;

   boost::asio::streambuf request;
   std::ostream request_stream(&request);
   auto host_header_value = format_host_header(url);
   // 将请求的URL封装成http包
   request_stream << "POST " << url.path << " HTTP/1.0\r\n";
   request_stream << "Host: " << host_header_value << "\r\n";
   request_stream << "content-length: " << postjson.size() << "\r\n";
   request_stream << "Accept: */*\r\n";
   request_stream << "Connection: close\r\n";
   request_stream << "\r\n";
   ....
         //和服务器建立连接
         do_connect(socket.next_layer(), url);
         socket.handshake(boost::asio::ssl::stream_base::client);
         //发送http报文,并获取返回结果
         re = do_txrx(socket, request, status_code);
         //try and do a clean shutdown; but swallow if this fails (other side could have already gave TCP the ax)
         try {socket.shutdown();} catch(...) {}

3、服务器接收消息流程
eos/programs/nodeos/main.cpp

//plugins/http_plugin/http_plugin.cpp
http_plugin::http_plugin():my(new http_plugin_impl()){}
   void http_plugin::plugin_startup() {
      if(my->listen_endpoint) {
         try {
            //1)注册http请求处理函数
            my->create_server_for_endpoint(*my->listen_endpoint, my->server);

            ilog("start listening for http requests");
            my->server.listen(*my->listen_endpoint);
            my->server.start_accept();
         } catch ( const fc::exception& e ){
            ;
         }
      }

      if(my->https_listen_endpoint) {
         try {
            my->create_server_for_endpoint(*my->https_listen_endpoint, my->https_server);
            my->https_server.set_tls_init_handler([this](websocketpp::connection_hdl hdl) -> ssl_context_ptr{
               return my->on_tls_init(hdl);
            });

            ilog("start listening for https requests");
            //2)监听socket通信端口
            my->https_server.listen(*my->https_listen_endpoint);
            //3)等待建立客户端远程连接
            my->https_server.start_accept();
         } catch ( const fc::exception& e ){
 //4)http请求处理函数从http报文中解析出URL地址(resource)、消息内容(body)
        template<class T>
         void create_server_for_endpoint(const tcp::endpoint& ep, websocketpp::server<detail::asio_with_stub_log<T>>& ws) {
            try {
             ...
               ws.set_http_handler([&](connection_hdl hdl) {
                  handle_http_request<T>(ws.get_con_from_hdl(hdl));
               });
            } ca
         }
map<string,url_handler>  url_handlers;
         template<class T>
         void handle_http_request(typename websocketpp::server<detail::asio_with_stub_log<T>>::connection_ptr con) {
            try {
               bool is_secure = con->get_uri()->get_secure();
               const auto& local_endpoint = con->get_socket().lowest_layer().local_endpoint();
               auto local_socket_host_port = local_endpoint.address().to_string() + ":" + std::to_string(local_endpoint.port());

               auto& req = con->get_request();
               const auto& host_str = req.get_header("Host");
               if (host_str.empty() || !host_is_valid(host_str, local_socket_host_port, is_secure)) {
                  con->set_status(websocketpp::http::status_code::bad_request);
                  return;
               }

               if( !access_control_allow_origin.empty()) {
                  con->append_header( "Access-Control-Allow-Origin", access_control_allow_origin );
              ...

               con->append_header( "Content-type", "application/json" );
               auto body = con->get_request_body();
               auto resource = con->get_uri()->get_resource();
               //5)在url_handlers集合中查找URL对应的回调函数
               auto handler_itr = url_handlers.find( resource );
               if( handler_itr != url_handlers.end()) {
                  con->defer_http_response();
                  //6)通过handler_itr->second调用处理函数
                  handler_itr->second( resource, body, [con]( auto code, auto&& body ) {
                     con->set_body( std::move( body ));
                     con->set_status( websocketpp::http::status_code::value( code ));
                     con->send_http_response();
                  } );

               } else {
                  wlog( "404 - not found: ${ep}", ("ep", resource));
                  error_results results{websocketpp::http::status_code::not_found,
                                        "Not Found", error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, "Unknown Endpoint" )), verbose_http_errors )};
                  con->set_body( fc::json::to_string( results ));
                  con->set_status( websocketpp::http::status_code::not_found );
               }
            } catch( ... ) {
               handle_exception<T>( con );
            }
         }

4、注册URL处理函数说明

//(1)url_handlers是一个URL和处理函数的键值对map集合,由class http\_plugin\_impl管理
//plugins/http_plugin/http_plugin.cpp
class http_plugin_impl {
       map<string,url_handler>  url_handlers;

//(2)其它插件模块通过add_api函数注册URL回调函数。
//plugins/http_plugin/include/eosio/http_plugin/http_plugin.hpp
class http_plugin : public appbase::plugin<http_plugin>
   {
      public:
        void add_handler(const string& url, const url_handler&);
        void add_api(const api_description& api) {
           for (const auto& call : api) 
              add_handler(call.first, call.second);
        }
   //实现:plugins/http_plugin/include/eosio/http_plugin/http_plugin.cpp
   void http_plugin::add_handler(const string& url, const url_handler& handler) {
      ilog( "add api url: ${c}", ("c",url) );
        app().get_io_service().post([=](){

        my->url_handlers.insert(std::make_pair(url,handler));
      });
   }
/**(3)eg:chain_api_plugin插件在启动函数中注册了以下URL回调函数,包括查询区块信息、处理交易数据:
 plugins/chain_api_plugin/chain_api_plugin.cpp */
#define CHAIN_RO_CALL(call_name) CALL(history, ro_api, history_apis::read_only, call_name) 
void chain_api_plugin::plugin_startup() {
   ilog( "starting chain_api_plugin" );
   my.reset(new chain_api_plugin_impl(app().get_plugin<chain_plugin>().chain()));
   auto ro_api = app().get_plugin<chain_plugin>().get_read_only_api();
   auto rw_api = app().get_plugin<chain_plugin>().get_read_write_api();

   app().get_plugin<http_plugin>().add_api({
      CHAIN_RO_CALL(get_info, 200l),
      CHAIN_RO_CALL(get_block, 200),
      CHAIN_RO_CALL(get_block_header_state, 200),
      CHAIN_RO_CALL(get_account, 200),
      CHAIN_RO_CALL(get_code, 200),
      CHAIN_RO_CALL(get_abi, 200),
      CHAIN_RO_CALL(get_raw_code_and_abi, 200),
      CHAIN_RO_CALL(get_table_rows, 200),
      CHAIN_RO_CALL(get_currency_balance, 200),
      CHAIN_RO_CALL(get_currency_stats, 200),
      CHAIN_RO_CALL(get_producers, 200),
      CHAIN_RO_CALL(get_producer_schedule, 200),
      CHAIN_RO_CALL(get_scheduled_transactions, 200),
      CHAIN_RO_CALL(abi_json_to_bin, 200),
      CHAIN_RO_CALL(abi_bin_to_json, 200),
      CHAIN_RO_CALL(get_required_keys, 200),
      CHAIN_RW_CALL_ASYNC(push_block, chain_apis::read_write::push_block_results, 202),
      CHAIN_RW_CALL_ASYNC(push_transaction, chain_apis::read_write::push_transaction_results, 202),
      CHAIN_RW_CALL_ASYNC(push_transactions, chain_apis::read_write::push_transactions_results, 202)
   });
}

5、生产区块流程
客户端发送 ”/V1/chain/push_transaction” URL地址和交易信息到服务器端,然后服务器调用URL对应的回调函数push_transaction将交易信息写入到一个待打包的区块(_pending_block)中。
位置:libraries/chain/controller.cpp

/**
    *  This is the entry point for new transactions to the block state. It will check authorization and
    *  determine whether to execute it now or to delay it. Lastly it inserts a transaction receipt into
    *  the pending block.
    */
   transaction_trace_ptr push_transaction( const transaction_metadata_ptr& trx,
                                           fc::time_point deadline,
                                           bool implicit,
                                           uint32_t billed_cpu_time_us)
   {
      EOS_ASSERT(deadline != fc::time_point(), transaction_exception, "deadline cannot be uninitialized");

      transaction_trace_ptr trace;
      try {
         transaction_context trx_context(self, trx->trx, trx->id);
         if ((bool)subjective_cpu_leeway && pending->_block_status == controller::block_status::incomplete) {
            trx_context.leeway = *subjective_cpu_leeway;
         }
         trx_context.deadline = deadline;
         trx_context.billed_cpu_time_us = billed_cpu_time_us;
         trace = trx_context.trace;
         try {
            if( implicit ) {
               trx_context.init_for_implicit_trx();
               trx_context.can_subjectively_fail = false;
            } else {
               trx_context.init_for_input_trx( trx->packed_trx.get_unprunable_size(),
                                               trx->packed_trx.get_prunable_size(),
                                               trx->trx.signatures.size());
            }

            if( trx_context.can_subjectively_fail && pending->_block_status == controller::block_status::incomplete ) {
               check_actor_list( trx_context.bill_to_accounts ); // Assumes bill_to_accounts is the set of actors authorizing the transaction
            }


            trx_context.delay = fc::seconds(trx->trx.delay_sec);

            if( !self.skip_auth_check() && !implicit ) {
               authorization.check_authorization(
                       trx->trx.actions,
                       trx->recover_keys( chain_id ),
                       {},
                       trx_context.delay,
                       [](){}
                       /*std::bind(&transaction_context::add_cpu_usage_and_check_time, &trx_context,
                                 std::placeholders::_1)*/,
                       false
               );
            }
            trx_context.exec();
            trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful

            auto restore = make_block_restore_point();

            if (!implicit) {
               transaction_receipt::status_enum s = (trx_context.delay == fc::seconds(0))
                                                    ? transaction_receipt::executed
                                                    : transaction_receipt::delayed;
               trace->receipt = push_receipt(trx->packed_trx, s, trx_context.billed_cpu_time_us, trace->net_usage);
               pending->_pending_block_state->trxs.emplace_back(trx);
            } else {
               transaction_receipt_header r;
               r.status = transaction_receipt::executed;
               r.cpu_usage_us = trx_context.billed_cpu_time_us;
               r.net_usage_words = trace->net_usage / 8;
               trace->receipt = r;
            }

            fc::move_append(pending->_actions, move(trx_context.executed));

            // call the accept signal but only once for this transaction
            if (!trx->accepted) {
               emit( self.accepted_transaction, trx);
               trx->accepted = true;
            }

            emit(self.applied_transaction, trace);


            if ( read_mode != db_read_mode::SPECULATIVE && pending->_block_status == controller::block_status::incomplete ) {
               //this may happen automatically in destructor, but I prefere make it more explicit
               trx_context.undo();
            } else {
               restore.cancel();
               trx_context.squash();
            }

            if (!implicit) {
               unapplied_transactions.erase( trx->signed_id );
            }
            return trace;
         } catch (const fc::exception& e) {
            trace->except = e;
            trace->except_ptr = std::current_exception();
         }

         if (!failure_is_subjective(*trace->except)) {
            unapplied_transactions.erase( trx->signed_id );
         }

         return trace;
      } FC_CAPTURE_AND_RETHROW((trace))
   } /// push_transaction

未完待续

猜你喜欢

转载自blog.csdn.net/http188188/article/details/81669734