EOS代码架构及分析(二)
EOS通信机制分析
客户端和服务器端的通信采用RESTful软件架构风格,服务器端的每个资源对应一个唯一的URL地址,客户端将URL地址封装成http请求发送到服务器端,请求对应的资源或者执行相应操作。
客户端发送消息流程
以转账为例,说明EOS消息处理流程。通过cleos客户端发起转账命令,在main函数中,解析transfer命令,通过create_transfer函数将交易发送者、交易接收者、token数量等信息封装成mutable_variant_object对象,然后调用send_action函数,将交易信息发送到服务器端打包进区块链。
# ./cleos transfer sender recipient amount memoprograms/cleos/main.cppmain(){…send_actions({create_transfer(sender, recipient, amount, memo)});…}void send_actions {auto result = push_actions( move(actions), extra_kcpu, compression);…
}fc::variant push_actions {signed_transaction trx;
trx.actions = std::forward<decltype(actions)>(actions);return push_transaction(trx, extra_kcpu, compression);}fc::variant push_transaction{trx.set_reference_block(ref_block_id);
// 发送 ”/V1/chain/push_transaction” URL地址到服务器端if (!tx_dont_broadcast) {return call(push_txn_func, packed_transaction(trx, compression));}}fc::variant call{try {return eosio::client::http::do_http_call( url, path, fc::variant(v) );}}fc::variant do_http_call {// 将请求的URL封装成http包request_stream << "POST " << path_prefix + path << " HTTP/1.0\r\n";
request_stream << "Host: " << server << "\r\n";request_stream << "content-length: " << postjson.size() << "\r\n";request_stream << "Accept: */*\r\n";request_stream << "Connection: close\r\n\r\n";request_stream << postjson;// 和服务器建立连接do_connect(socket, server, port);// 发送http报文,并获取返回结果re = do_txrx(socket, request, status_code);
}
服务器接收消息流程
nodeos服务器先通过http_plugin插件接收客户端发过来的http请求报文,然后解析出请求的URL地址和数据信息,然后调用对应的回调函数处理,并将结果返回给cleos客户端。
HTTP消息处理流程
在nodeos的main函数中启动http_plugin插件,注册处理http请求的回调函数(handle_http_request),然后监听socket通信端口,等待建立客户端远程连接。
void http_plugin::plugin_startup() {// 注册http请求处理函数my->create_server_for_endpoint(*my->https_listen_endpoint, my->https_server);
// 监听socket通信端口my->https_server.listen(*my->https_listen_endpoint);// 等待建立客户端远程连接my->https_server.start_accept();}void create_server_for_endpoint{ws.set_http_handler([&](connection_hdl hdl) {
handle_http_request<T>(ws.get_con_from_hdl(hdl));});}
http请求处理函数从http报文中解析出URL地址(resource)、消息内容(body),然后在url_handlers集合中查找URL对应的回调函数,最后通过handler_itr->second调用处理函数。
void handle_http_request {…auto body = con->get_request_body();
auto resource = con->get_uri()->get_resource();auto handler_itr = url_handlers.find(resource);if(handler_itr != url_handlers.end()) {handler_itr->second(resource, body, [con](int code, string body) {con->set_body(body);
con->set_status(websocketpp::http::status_code::value(code));});}…}
注册URL处理函数
url_handlers是一个URL和处理函数的键值对map集合,由class http_plugin_impl管理,其它插件模块通过add_api函数注册URL回调函数。
plugins/http_plugin/http_plugin.cppclass http_plugin_impl {map<string,url_handler> url_handlers;
...}void add_api(const api_description& api) {for (const auto& call : api)
add_handler(call.first, call.second);}void http_plugin::add_handler {…my->url_handlers.insert(std::make_pair(url,handler);
}
例如,chain_api_plugin插件在启动函数中注册了以下URL回调函数,包括查询区块信息、处理交易数据:
void chain_api_plugin::plugin_startup() {app().get_plugin<http_plugin>().add_api({
CHAIN_RO_CALL(get_info, 200),CHAIN_RO_CALL(get_block, 200),…CHAIN_RW_CALL(push_transaction, 202),
CHAIN_RW_CALL(push_transactions, 202)});}
生产区块流程
客户端发送 ”/V1/chain/push_transaction” URL地址和交易信息到服务器端,然后服务器调用URL对应的回调函数push_transaction将交易信息写入到一个待打包的区块(_pending_block)中。
chain_controller::push_transaction {if( !_pending_block ) {
_start_pending_block();}…return _push_transaction(trx);}chain_controller::_push_transaction {_pending_block->input_transactions.emplace_back(packed_trx);
}
一个区块可以包含很多个transaction,通过input_transactions将这些transaction管理起来,随后由producer_plugin插件将区块打包进区块链中,然后向其它nodeos节点广播区块信息。
struct signed_block : public signed_block_summary {…vector<packed_transaction> input_transactions;
}
producer_plugin插件启动后,通过schedule_production_loop函数循环生产区块。EOS采用DPoS(委托股权证明)算法,先由EOS持有者(股东)选出21个区块生产者(董事会成员),区块通过这21个生产者轮流产生,每3秒出一个区块,类似操作系统的时间片概念,每个时间片对应一个唯一的生产者,当时间片到来时才能打包区块。
DPoS算法和比特币的POW算法有很大区别,在POW算法中,矿工只要发现交易信息就开始打包区块,而且需要消耗巨大的算力,而且交易确认时间很长。而DPoS算法则通过提前选举出可信节点,避免了信任证明的开销,同时生产者数量的减少(21个)也极大提升了交易确认效率,防止性能差的节点拖慢整个区块链生产速度。DPoS的时间片机制能够保证可信区块链的长度始终比恶意分叉的区块链长(恶意节点数量不大于1/3总节点数量),例如,节点B想自己构造一个分叉链,但是由于每9秒才能产生一个区块,所以始终没有主链长。
void producer_plugin::plugin_startup(){
…my->schedule_production_loop();…}
计算出现在距离下一个区块时间片的时间间隔time_to_next_block_time,然后启动一个定时器,当下一个时间片到来时调用block_production_loop函数生产区块。
void producer_plugin_impl::schedule_production_loop() {int64_t time_to_next_block_time = (config::block_interval_us) - (now.time_since_epoch().count() % (config::block_interval_us) );
_timer.expires_from_now( boost::posix_time::microseconds(time_to_next_block_time) );_timer.async_wait( [&](const boost::system::error_code&){ block_production_loop(); } );}
调用maybe_produce_block函数生产区块,从函数名的maybe可知,不一定能够生产出区块,只是进行尝试,然后处理结果,最后递归调用schedule_production_loop函数进入下一次循环。
producer_plugin_impl::block_production_loop() {result = maybe_produce_block(capture);
…schedule_production_loop();return result;
}
获取当前时间对应的生产者,然后调用chain.generate_block函数生产区块,完成后通过broadcast_block函数向其它节点广播区块信息。
producer_plugin_impl::maybe_produce_block {uint32_t slot = chain.get_slot_at_time( now );…auto scheduled_producer = chain.get_scheduled_producer( slot );…auto block = chain.generate_block(
scheduled_time,scheduled_producer,private_key_itr->second,_production_skip_flags);app().get_plugin<net_plugin>().broadcast_block(block);return block_production_condition::produced;}chain_controller::generate_block(…return _generate_block( when, producer, block_signing_private_key );
}
将生产者信息更新到之前的待打包区块_pending_block中,例如,区块时间戳、区块编号、生产者状态等等,最后将区块写入本地区块链中。
chain_controller::_generate_block {_pending_block->timestamp = when;
_pending_block->producer = producer_obj.owner;_pending_block->previous = head_block_id();…if( !(skip & skip_producer_signature) )_pending_block->sign( block_signing_key );_finalize_block( *_pending_block_trace, producer_obj );}
至此,一次完整的区块处理流程就完成了,后面不断重复打包过程,随着时间推移,形成一个不可逆转的区块链。
EOS代码架构及分析(二)相关推荐
- EOS代码架构及分析(一)
EOS简介 EOS(Enterprise Operation System),企业操作系统,是为企业级分布式应用设计的一款区块链操作系统.相比于目前区块链平台性能低.开发难度大以及手续费高等问题,EO ...
- EOS代码架构及分析(四)
什么是智能合约 在解释智能合约前,我们先来看看传统合约的形态.合约的本质是由一系列条款组成,每个条款由若干条规则组成,通过向条款中输入固定的参数,会输出固定的结果.传统合约需要由双方共同参与签署确认, ...
- Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
关键词:蓝牙blueZ UART HCI_UART H4 HCI L2CAP RFCOMM 版本:基于android4.2之前版本 bluez内核:linux/linux3.08 系统:an ...
- 5G学习-OAI代码架构分析
文章目录 1 OAI代码架构分析 1.1 简介 1.2 硬件部分 1.3 软件部分 1.3.2 代码结构 1.3.3 代码结构说明 1.4 参考文献 1 OAI代码架构分析 1.1 简介 官方网站: ...
- 1小时学会:最简单的iOS直播推流(二)代码架构概述
最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有,愿意懂直播技术的同学快来看!! 源 ...
- Android4.0图库Gallery2代码分析(二) 数据管理和数据加载
Android4.0图库Gallery2代码分析(二) 数据管理和数据加载 2012-09-07 11:19 8152人阅读 评论(12) 收藏 举报 代码分析android相册优化工作 Androi ...
- Android架构实例分析之编写hello驱动的HAL层代码
Android架构实例分析之编写hello驱动的HAL层代码 摘要: HAL层中文名称又叫硬件抽象层,可以理解我Linux驱动的应用层.本文实现了一个简单的hello HAL的代码,衔接hello驱动 ...
- 微信、陌陌的架构方案分析(LBS之二)
目标 解决大型应用(微信.陌陌级别)中,用户经纬度在不断更新,用户查找频繁的问题.(每分钟1000W级) 方案A 本方案前,请先阅读 http://www.alivenode.com/index.ph ...
- ARMv8架构u-boot启动流程详细分析(二)
文章目录 1 u-boot在汇编启动阶段对系统的一些初始化 1.1 启动前为后续流程做的一些平台相关操作 1.2 开启地址无关后的重定位地址操作 1.3 进入_main之前系统寄存器初始化和从核的引导 ...
最新文章
- CUDA 8混合精度编程
- VMware VSphere 虚拟化云计算学习配置笔记(一)
- 原生app跳html页面传值,HTML页面跳转及参数传递问题
- Linux 查看进程在哪个CPU上运行
- 一次请求到php都经过了哪些步骤,PHP高级之一次请求处理过程或生命周期详解
- H5 六边形消除游戏开发
- 经典的C++面试题目
- 图解Ubuntu中pidgin登陆IRC
- mybatis依赖_这大概就是公司一直用Mybatis的原因!真的太强了
- iphone模拟器_应用日报 | Xcode 现身 5.4 英寸 iPhone 模拟器,和平精英上线特斯拉皮肤...
- sql server 用户'sa'登录失败(错误18456)(转载)
- 7 种 JVM 垃圾收集器,Java语言实现核心,看完我跪了
- windows程序设计之简单界面入门
- 360浏览器html文件无图标,电脑桌面360浏览器图标不见了解决方法图文教程
- chrome浏览器多开工具
- 关于安装软件时x86 ,x64,x86_64,ARM 64, ARM 32 的选择
- M1芯片安装CleanMyMac X4.7.4的方法(附下载)M1芯片安装那个CleanMyMac X版本?CleanMyMac X已完美支持M1芯片安装 支持big sur系统
- Mac新手必备技巧之如何关闭Mac屏幕亮度自动调节功能
- 在电脑上查看WIFI的密码
- 万向球头的锁紧结构图_联动锁紧球关节万向杆的制作方法