写在前面的话

看EOS源码,就像穿越千座大山,感觉已经搞懂了一个地方,但是随之而来的是更大的谜团。

公司要求我了解智能合约的相关原理,我专心用EOS的合约作为调研对象,但是到现在为止,还是有许多很大的谜团在等待着我:

1.EOS合约的结构是怎样的,有什么是必须的,什么是可选的,为什么必须的就是的必须?

2.我们知道,合约通过eosio-cpp 编译成wasm格式和可读模式abi文件,但是编译的细节又是怎样?

3.合约是怎么部署的,部署后的文件是什么类型,存储在哪里,存储的内容又是什么?是怎么生成一个hash码的?

4.怎么调用合约,合约之间的接口又是什么形式存储。

5.更改合约内容后如何重新部署

6.如何快速升级合约,是否需要升级合约?

等等。

所以,不得不去看源码,但是如何找到线头可以更快的去了解这些东西,这是非常重要的,本文旨在尝试通过笔者特有的角度试着解答这些谜题。

EOS关联的技术确实很多很多,多到不可能一个人能够知道所有,谁敢说他精通LLVM的同时,对CMAKE也非常了解,即便对CMAKE也很精通,但是C++11,C++14甚至C++17呢?

或许更大的谜题是Boost,是wasm。我没法解决这些问题,因此当我看到有这些相关的知识点时,我只能试着避过它们,对他们实现的细节,性能等我不去深究,我只在我看到的地方大概知道是干嘛的就可以了,我相信,别人也是这样干的。好了,我们一起来深究智能合约的谜团吧。

从什么地方开始?我试着从链和块的生成开始

链和块是如何生成的

通过我之前的文档,我们已经知道EOS是如何部署的,有关细节请参考:

eosio_build.sh 执行过程eosio_build_centos.sh执行过程eosio_install.sh执行过程

这里面已经完全安装了工具包和eos软件,也就是说,当执行完eosio_build.sh 和 eosio_install.sh后,一切都已经准备好了,包括LLVM,包括Boost,也许还包括MongoDB。但是还有些东西没有准备好,比如wasm 虚拟机,比如block和chain,看过官方文档的人知道,要把这一切准备好,只需要执行这样的命令:

nodeos -e -p eosio \
--plugin eosio::producer_plugin \
--plugin eosio::chain_api_plugin \
--plugin eosio::http_plugin \
--plugin eosio::history_plugin \
--plugin eosio::history_api_plugin \
--filter-on="*" \
--access-control-allow-origin='*' \
--contracts-console \
--http-validate-host=false \
--verbose-http-errors >> nodeos.log 2>&1 &

这很关键,也许,突破口就在这里,我们只要明白命令实现的每一个细节就可以了。

nodeos是管理节点的,在${WORKSPACE}\programs\nodeos目录下,这里面有关于nodeos的所有内容,先从CMakeLists.txt文件入手,第一行就是这个

add_executable( ${NODE_EXECUTABLE_NAME} main.cpp )

其中${NODE_EXECUTABLE_NAME}就是nodeos,这个只要ctrl+r就知道了,透露下,这里的定义是在主CMakeLists.txt里面的,cleos和keosd都是在主CMakeLists.txt定义,内容如下

set( CLI_CLIENT_EXECUTABLE_NAME cleos )
set( NODE_EXECUTABLE_NAME nodeos )
set( KEY_STORE_EXECUTABLE_NAME keosd )

 ps:

    add_executable:引入一个名为< name>的可执行目标,该目标会由调用该命令时在源文件列表中指定的源文件来构建

    set:用来显式的定义变量

也就是说,当使用nodeos命令时,会执行main.cpp里面的方法,也就是执行main方法,现在开始分析main方法,关键代码如下

      app().set_version(eosio::nodeos::config::version);

      auto root = fc::app_path();
      app().set_default_data_dir(root / "eosio" / nodeos::config::node_executable_name / "data" );
      app().set_default_config_dir(root / "eosio" / nodeos::config::node_executable_name / "config" );
      http_plugin::set_defaults({
         .default_unix_socket_path = "",
         .default_http_port = 8888
      });
      if(!app().initialize<chain_plugin, net_plugin, producer_plugin>(argc, argv))
         return INITIALIZE_FAIL;
      initialize_logging();
      ilog("${name} version ${ver}", ("name", nodeos::config::node_executable_name)("ver", app().version_string()));
      ilog("${name} using configuration file ${c}", ("name", nodeos::config::node_executable_name)("c", app().full_config_file_path().string()));
      ilog("${name} data directory is ${d}", ("name", nodeos::config::node_executable_name)("d", app().data_dir().string()));
      app().startup();
      app().exec();
01-01 04:08