当前位置: 首页 > news >正文

EOS源码解析 eosio账号默认合约

EOS 系统账号的创建

     当EOS启动时,程序会去调用create_native_account 创建eosio等账号,但是创建一般账号时,却不是调用create_native_account,那此时cleos create account是如何创建账号的呢, 其实它也是调用eosio智能合约的newaccount,但eosio默认的智能合约是由代码提前约定好的,而不是eosio.system智能合约。

eosio的默认合约是由eos_contract_abi.cppeos_contract_abi.cpp这两个文件约定好的。

eos_contract_abi.cpp 是构建智能合约的abi信息的。

// TODO add ricardian contracts
   eos_abi.actions.push_back( action_def{name("newaccount"), "newaccount",""} );
   eos_abi.actions.push_back( action_def{name("setcode"), "setcode",""} );
   eos_abi.actions.push_back( action_def{name("setabi"), "setabi",""} );
   eos_abi.actions.push_back( action_def{name("updateauth"), "updateauth",""} );
   eos_abi.actions.push_back( action_def{name("deleteauth"), "deleteauth",""} );
   eos_abi.actions.push_back( action_def{name("linkauth"), "linkauth",""} );
   eos_abi.actions.push_back( action_def{name("unlinkauth"), "unlinkauth",""} );
   eos_abi.actions.push_back( action_def{name("canceldelay"), "canceldelay",""} );
   eos_abi.actions.push_back( action_def{name("onerror"), "onerror",""} );
   eos_abi.actions.push_back( action_def{name("onblock"), "onblock",""} );
复制代码

eos_contract.cpp 是实现了abi定义的action方法。

/**
 *  This method is called assuming precondition_system_newaccount succeeds a
 */
void apply_eosio_newaccount(apply_context& context) {
   auto create = context.act.data_as<newaccount>();
   try {
   context.require_authorization(create.creator);
//   context.require_write_lock( config::eosio_auth_scope );
   auto& authorization = context.control.get_mutable_authorization_manager();
  //判断公钥是否合法。
   EOS_ASSERT( validate(create.owner), action_validate_exception, "Invalid owner authority");
   EOS_ASSERT( validate(create.active), action_validate_exception, "Invalid active authority");

   auto& db = context.db;

   auto name_str = name(create.name).to_string();
  //判断account name的合法性
   EOS_ASSERT( !create.name.empty(), action_validate_exception, "account name cannot be empty" );
   EOS_ASSERT( name_str.size() <= 12, action_validate_exception, "account names can only be 12 chars long" );

   // Check if the creator is privileged
   //只有eosio才能创建eosio.为前缀的账号。
   const auto &creator = db.get<account_object, by_name>(create.creator);
   if( !creator.privileged ) {
      EOS_ASSERT( name_str.find( "eosio." ) != 0, action_validate_exception,
                  "only privileged accounts can have names that start with 'eosio.'" );
   }
  //判断用户名是否存在。
   auto existing_account = db.find<account_object, by_name>(create.name);
   EOS_ASSERT(existing_account == nullptr, account_name_exists_exception,
              "Cannot create account named ${name}, as that name is already taken",
              ("name", create.name));

   const auto& new_account = db.create<account_object>([&](auto& a) {
      a.name = create.name;
      a.creation_date = context.control.pending_block_time();
   });

   db.create<account_sequence_object>([&](auto& a) {
      a.name = create.name;
   });

   for( const auto& auth : { create.owner, create.active } ){
      validate_authority_precondition( context, auth );
   }

   const auto& owner_permission  = authorization.create_permission( create.name, config::owner_name, 0,
                                                                    std::move(create.owner) );
   const auto& active_permission = authorization.create_permission( create.name, config::active_name, owner_permission.id,
                                                                    std::move(create.active) );

   context.control.get_mutable_resource_limits_manager().initialize_account(create.name);

   int64_t ram_delta = config::overhead_per_account_ram_bytes;
   ram_delta += 2*config::billable_size_v<permission_object>;
   ram_delta += owner_permission.auth.get_billable_size();
   ram_delta += active_permission.auth.get_billable_size();

   context.trx_context.add_ram_usage(create.name, ram_delta);

} FC_CAPTURE_AND_RETHROW( (create) ) }
复制代码

本章通过newaccount来做示例。

我们再来找找EOS 是在哪里给eosio设定了默认合约。直接全局搜索eosio_contract.

可以发现在创建eosio账号的时候,便给他设置了abi信息,但是action的wasm是如何导入的呢。

找找eosio_contract.cpp的方法在哪里引用到,会在controller.cpp找到,因为他是通过宏去连接字符,所以全局搜索字符无法找到。

#define SET_APP_HANDLER( receiver, contract, action) \
   set_apply_handler( #receiver, #contract, #action, &BOOST_PP_CAT(apply_, BOOST_PP_CAT(contract, BOOST_PP_CAT(_,action) ) ) )

   SET_APP_HANDLER( eosio, eosio, newaccount );
   SET_APP_HANDLER( eosio, eosio, setcode );
   SET_APP_HANDLER( eosio, eosio, setabi );
   SET_APP_HANDLER( eosio, eosio, updateauth );
   SET_APP_HANDLER( eosio, eosio, deleteauth );
   SET_APP_HANDLER( eosio, eosio, linkauth );
   SET_APP_HANDLER( eosio, eosio, unlinkauth );
/*
   SET_APP_HANDLER( eosio, eosio, postrecovery );
   SET_APP_HANDLER( eosio, eosio, passrecovery );
   SET_APP_HANDLER( eosio, eosio, vetorecovery );
*/

   SET_APP_HANDLER( eosio, eosio, canceldelay );




 void set_apply_handler( account_name receiver, account_name contract, action_name action, apply_handler v ) {
      apply_handlers[receiver][make_pair(contract,action)] = v;
   }

const apply_handler* controller::find_apply_handler( account_name receiver, account_name scope, action_name act ) const
{
   auto native_handler_scope = my->apply_handlers.find( receiver );
   if( native_handler_scope != my->apply_handlers.end() ) {
      auto handler = native_handler_scope->second.find( make_pair( scope, act ) );
      if( handler != native_handler_scope->second.end() )
         return &handler->second;
   }
   return nullptr;
}
复制代码

按照这样的顺序可以得知,eosio的默认合约是通过find_apply_handler来调用的。找找哪里调用了find_apply_handler。

action_trace apply_context::exec_one()
{
   auto start = fc::time_point::now();

   const auto& cfg = control.get_global_properties().configuration;
   try {
      const auto& a = control.get_account( receiver );
      privileged = a.privileged;
      //寻找eosio默认智能合约方法句柄
      auto native = control.find_apply_handler( receiver, act.account, act.name );
      if( native ) {
         if( trx_context.can_subjectively_fail && control.is_producing_block()) {
            control.check_contract_list( receiver );
            control.check_action_list( act.account, act.name );
         }
         (*native)( *this );
      }

      if( a.code.size() > 0
          && !(act.account == config::system_account_name && act.name == N( setcode ) &&
               receiver == config::system_account_name)) {
         if( trx_context.can_subjectively_fail && control.is_producing_block()) {
            control.check_contract_list( receiver );
            control.check_action_list( act.account, act.name );
         }
         try {
            control.get_wasm_interface().apply( a.code_version, a.code, *this );
         } catch( const wasm_exit& ) {}
      }

   } FC_RETHROW_EXCEPTIONS(warn, "pending console output: ${console}", ("console", _pending_console_output.str()))

   action_receipt r;
   r.receiver         = receiver;
   r.act_digest       = digest_type::hash(act);
   r.global_sequence  = next_global_sequence();
   r.recv_sequence    = next_recv_sequence( receiver );

   const auto& account_sequence = db.get<account_sequence_object, by_name>(act.account);
   r.code_sequence    = account_sequence.code_sequence;
   r.abi_sequence     = account_sequence.abi_sequence;

   for( const auto& auth : act.authorization ) {
      r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor );
   }

   action_trace t(r);
   t.trx_id = trx_context.id;
   t.act = act;
   t.console = _pending_console_output.str();

   trx_context.executed.emplace_back( move(r) );

   if ( control.contracts_console() ) {
      print_debug(receiver, t);
   }

   reset_console();

   t.elapsed = fc::time_point::now() - start;
   return t;
}
复制代码

EOS 在push action的时候会先去找找find_apply_handler有没有该action,有的话通过它找到方法句柄来执行。没有的话直接通过wasm去执行action。

eosio账号的默认合约的调用过程已经解析完毕了。

结论

     eosio 的初始合约是通过硬编码部署的,实际上也不是通过虚拟机运行的。从代码上也可以看出,即使部署了 eosio.system 智能合约,eosio 的默认合约还是会执行。

转载于:https://juejin.im/post/5be30c00e51d45305c2ce4c8

相关文章:

  • 项目总结11:Centos部署JDK+Tomcat+MySQL文档(阿里云-网易云-华为云)
  • Oracle Procedure模板
  • 扫呗扫码点餐,如何在扫呗后台给这个商户配一下支付授权地址
  • 外网访问内网Resin
  • Jquery添加元素(append,prepend,after,before四种方法区别对比)
  • 面向对象类的解析
  • 关于Android全面屏虚拟导航栏的适配总结
  • 89元尝鲜智能家居 天猫精灵方糖智联3件套简评
  • Redis命令——哈希(Hash)
  • Material Design 设计规范总结(1)
  • Git内部原理之Git引用
  • 9号团队第一次会议
  • 数据科学 第 3 章 11 字符串处理
  • Elasticsearch 优化
  • 深入理解多线程(三)—— Java的对象头
  • php的引用
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 30天自制操作系统-2
  • Android Studio:GIT提交项目到远程仓库
  • Asm.js的简单介绍
  • PHP CLI应用的调试原理
  • Python语法速览与机器学习开发环境搭建
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • SQLServer插入数据
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 服务器从安装到部署全过程(二)
  • 高度不固定时垂直居中
  • 基于axios的vue插件,让http请求更简单
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • ionic异常记录
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (13):Silverlight 2 数据与通信之WebRequest
  • (floyd+补集) poj 3275
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (三)mysql_MYSQL(三)
  • (转)树状数组
  • (转载)hibernate缓存
  • .naturalWidth 和naturalHeight属性,
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .Net 4.0并行库实用性演练
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .NET4.0并行计算技术基础(1)
  • .NET单元测试
  • .net开发引用程序集提示没有强名称的解决办法
  • ?
  • ??在JSP中,java和JavaScript如何交互?
  • @manytomany 保存后数据被删除_[Windows] 数据恢复软件RStudio v8.14.179675 便携特别版...
  • [@Controller]4 详解@ModelAttribute
  • [BUUCTF 2018]Online Tool(特详解)
  • [BZOJ 1032][JSOI2007]祖码Zuma(区间Dp)